diff --git a/.travis.yml b/.travis.yml index 3ee298947a8..4926ede122b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,10 +13,9 @@ notifications: branches: only: - master - - "5.16" + - "5.18" # enable building tags - /^v\d+\.\d+(\.\d+)?(-\S*)?$/ - - "5.17" cache: yarn: true diff --git a/CHANGELOG.md b/CHANGELOG.md index cc753e8cd68..ccc42011219 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,16 @@ -# 5.17.3 - - Changes from 5.17.2: - - Bugfixes: - - FIXED: Increased probability of returning alternatives and quality of the ones returned for MLD. [#5048](https://github.com/Project-OSRM/osrm-backend/issues/5048) +# 5.18.0 + - Changes from 5.17.0: + - Features: + - ADDED: `table` plugin now optionally returns `distance` matrix as part of response [#4990](https://github.com/Project-OSRM/osrm-backend/pull/4990) + - ADDED: New optional parameter `annotations` for `table` that accepts `distance`, `duration`, or both `distance,duration` as values [#4990](https://github.com/Project-OSRM/osrm-backend/pull/4990) + - Infrastructure: + - ADDED: Updated libosmium and added protozero and vtzero libraries [#5037](https://github.com/Project-OSRM/osrm-backend/pull/5037) + - CHANGED: Use vtzero library in tile plugin [#4686](https://github.com/Project-OSRM/osrm-backend/pull/4686) + - Profile: + - ADDED: Bicycle profile now returns classes for ferry and tunnel routes. [#5054](https://github.com/Project-OSRM/osrm-backend/pull/5054) + - ADDED: Bicycle profile allows to exclude ferry routes (default to not enabled) [#5054](https://github.com/Project-OSRM/osrm-backend/pull/5054) -# 5.17.2 +# 5.17.1 - Changes from 5.17.0: - Bugfixes: - FIXED: Do not combine a segregated edge with a roundabout [#5039](https://github.com/Project-OSRM/osrm-backend/issues/5039) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e6e40828d3..e1767b961d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,7 +167,7 @@ add_executable(osrm-customize src/tools/customize.cpp) add_executable(osrm-contract src/tools/contract.cpp) add_executable(osrm-routed src/tools/routed.cpp $ $) add_executable(osrm-datastore src/tools/store.cpp $ $) -add_library(osrm src/osrm/osrm.cpp $ $ $ $ ) +add_library(osrm src/osrm/osrm.cpp $ $ $ $) add_library(osrm_contract src/osrm/contractor.cpp $ $) add_library(osrm_extract src/osrm/extractor.cpp $ $ $) add_library(osrm_guidance $ $) @@ -416,11 +416,30 @@ if(UNIX AND NOT APPLE) set(MAYBE_RT_LIBRARY -lrt) endif() -# Disallow deprecated protozero APIs -add_definitions(-DPROTOZERO_STRICT_API) - find_package(Threads REQUIRED) +# Third-party libraries +set(RAPIDJSON_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rapidjson/include") +include_directories(SYSTEM ${RAPIDJSON_INCLUDE_DIR}) + +set(MICROTAR_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src") +include_directories(SYSTEM ${MICROTAR_INCLUDE_DIR}) + +set(MBXGEOM_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/geometry.hpp-0.9.2/include") +include_directories(SYSTEM ${MBXGEOM_INCLUDE_DIR}) +set(CHEAPRULER_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cheap-ruler-cpp-2.5.4/include") +include_directories(SYSTEM ${CHEAPRULER_INCLUDE_DIR}) + +add_library(MICROTAR OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src/microtar.c") +set_property(TARGET MICROTAR PROPERTY POSITION_INDEPENDENT_CODE ON) + +set(PROTOZERO_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/protozero/include") +include_directories(SYSTEM ${PROTOZERO_INCLUDE_DIR}) + +set(VTZERO_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/vtzero/include") +include_directories(SYSTEM ${VTZERO_INCLUDE_DIR}) + + # if mason is enabled no find_package calls are made # to ensure that we are only compiling and linking against # fully portable mason packages @@ -554,14 +573,6 @@ else() include_directories(SYSTEM ${OSMIUM_INCLUDE_DIR}) endif() -set(RAPIDJSON_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rapidjson/include") -include_directories(SYSTEM ${RAPIDJSON_INCLUDE_DIR}) - -set(MICROTAR_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src") -include_directories(SYSTEM ${MICROTAR_INCLUDE_DIR}) -add_library(MICROTAR OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src/microtar.c") -set_property(TARGET MICROTAR PROPERTY POSITION_INDEPENDENT_CODE ON) - # prefix compilation with ccache by default if available and on clang or gcc if(ENABLE_CCACHE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")) find_program(CCACHE_FOUND ccache) @@ -862,4 +873,4 @@ if (ENABLE_NODE_BINDINGS) endforeach() add_library(check-headers STATIC EXCLUDE_FROM_ALL ${sources}) set_target_properties(check-headers PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${check_headers_dir}) -endif() +endif() \ No newline at end of file diff --git a/README.md b/README.md index 4afc7836714..4f3540e0f96 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ High performance routing engine written in C++14 designed to run on OpenStreetMa The following services are available via HTTP API, C++ library interface and NodeJs wrapper: - Nearest - Snaps coordinates to the street network and returns the nearest matches - Route - Finds the fastest route between coordinates -- Table - Computes the duration of the fastest route between all pairs of supplied coordinates +- Table - Computes the duration or distances of the fastest route between all pairs of supplied coordinates - Match - Snaps noisy GPS traces to the road network in the most plausible way - Trip - Solves the Traveling Salesman Problem using a greedy heuristic - Tile - Generates Mapbox Vector Tiles with internal routing metadata diff --git a/cucumber.js b/cucumber.js index b0276ceed45..288e3ba5aea 100644 --- a/cucumber.js +++ b/cucumber.js @@ -3,5 +3,5 @@ module.exports = { verify: '--strict --tags ~@stress --tags ~@todo --tags ~@mld-only -f progress --require features/support --require features/step_definitions', todo: '--strict --tags @todo --require features/support --require features/step_definitions', all: '--strict --require features/support --require features/step_definitions', - mld: '--strict --tags ~@stress --tags ~@todo --require features/support --require features/step_definitions -f progress' + mld: '--strict --tags ~@stress --tags ~@todo --tags ~@ch --require features/support --require features/step_definitions -f progress' }; diff --git a/docs/http.md b/docs/http.md index b14b0a341db..3cb2b811bd6 100644 --- a/docs/http.md +++ b/docs/http.md @@ -222,13 +222,13 @@ curl 'http://router.project-osrm.org/route/v1/driving/13.388860,52.517037;13.397 ### Table service -Computes the duration of the fastest route between all pairs of supplied coordinates. +Computes the duration of the fastest route between all pairs of supplied coordinates. Returns the durations or distances or both between the coordinate pairs. Note that the distances are not the shortest distance between two coordinates, but rather the distances of the fastest routes. Duration is in seconds and distances is in meters. ```endpoint -GET /table/v1/{profile}/{coordinates}?{sources}=[{elem}...];&destinations=[{elem}...] +GET /table/v1/{profile}/{coordinates}?{sources}=[{elem}...];&{destinations}=[{elem}...]&annotations={duration|distance|duration,distance} ``` -**Coordinates** +**Options** In addition to the [general options](#general-options) the following options are supported for this service: @@ -236,6 +236,8 @@ In addition to the [general options](#general-options) the following options are |------------|--------------------------------------------------|---------------------------------------------| |sources |`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as source. | |destinations|`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as destination.| +|annotations |`duration` (default), `distance`, or `duration,distance`|Return the requested table or tables in response. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned. +| Unlike other array encoded options, the length of `sources` and `destinations` can be **smaller or equal** to number of input locations; @@ -253,14 +255,23 @@ sources=0;5;7&destinations=5;1;4;2;3;6 #### Example Request ```curl -# Returns a 3x3 matrix: +# Returns a 3x3 duration matrix: curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219' -# Returns a 1x3 matrix +# Returns a 1x3 duration matrix curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?sources=0' -# Returns a asymmetric 3x2 matrix with from the polyline encoded locations `qikdcB}~dpXkkHz`: +# Returns a asymmetric 3x2 duration matrix with from the polyline encoded locations `qikdcB}~dpXkkHz`: curl 'http://router.project-osrm.org/table/v1/driving/polyline(egs_Iq_aqAppHzbHulFzeMe`EuvKpnCglA)?sources=0;1;3&destinations=2;4' + +# Returns a 3x3 duration matrix: +curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?annotations=duration' + +# Returns a 3x3 distance matrix for CH: +curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?annotations=distance' + +# Returns a 3x3 duration matrix and a 3x3 distance matrix for CH: +curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?annotations=distance,duration' ``` **Response** @@ -268,17 +279,115 @@ curl 'http://router.project-osrm.org/table/v1/driving/polyline(egs_Iq_aqAppHzbHu - `code` if the request was successful `Ok` otherwise see the service dependent and general status codes. - `durations` array of arrays that stores the matrix in row-major order. `durations[i][j]` gives the travel time from the i-th waypoint to the j-th waypoint. Values are given in seconds. Can be `null` if no route between `i` and `j` can be found. +- `distances` array of arrays that stores the matrix in row-major order. `distances[i][j]` gives the travel distance from + the i-th waypoint to the j-th waypoint. Values are given in meters. Can be `null` if no route between `i` and `j` can be found. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned. - `sources` array of `Waypoint` objects describing all sources in order - `destinations` array of `Waypoint` objects describing all destinations in order In case of error the following `code`s are supported in addition to the general ones: -| Type | Description | -|-------------------|-----------------| +| Type | Description | +|------------------|-----------------| | `NoTable` | No route found. | +| `NotImplemented` | This request is not supported | All other properties might be undefined. +#### Example Response + +```json +{ + "sources": [ + { + "location": [ + 13.3888, + 52.517033 + ], + "hint": "PAMAgEVJAoAUAAAAIAAAAAcAAAAAAAAArss0Qa7LNEHiVIRA4lSEQAoAAAAQAAAABAAAAAAAAADMAAAAAEzMAKlYIQM8TMwArVghAwEA3wps52D3", + "name": "Friedrichstraße" + }, + { + "location": [ + 13.397631, + 52.529432 + ], + "hint": "WIQBgL6mAoAEAAAABgAAAAAAAAA7AAAAhU6PQHvHj0IAAAAAQbyYQgQAAAAGAAAAAAAAADsAAADMAAAAf27MABiJIQOCbswA_4ghAwAAXwVs52D3", + "name": "Torstraße" + }, + { + "location": [ + 13.428554, + 52.523239 + ], + "hint": "7UcAgP___38fAAAAUQAAACYAAABTAAAAhSQKQrXq5kKRbiZCWJo_Qx8AAABRAAAAJgAAAFMAAADMAAAASufMAOdwIQNL58wA03AhAwMAvxBs52D3", + "name": "Platz der Vereinten Nationen" + } + ], + "durations": [ + [ + 0, + 192.6, + 382.8 + ], + [ + 199, + 0, + 283.9 + ], + [ + 344.7, + 222.3, + 0 + ] + ], + "destinations": [ + { + "location": [ + 13.3888, + 52.517033 + ], + "hint": "PAMAgEVJAoAUAAAAIAAAAAcAAAAAAAAArss0Qa7LNEHiVIRA4lSEQAoAAAAQAAAABAAAAAAAAADMAAAAAEzMAKlYIQM8TMwArVghAwEA3wps52D3", + "name": "Friedrichstraße" + }, + { + "location": [ + 13.397631, + 52.529432 + ], + "hint": "WIQBgL6mAoAEAAAABgAAAAAAAAA7AAAAhU6PQHvHj0IAAAAAQbyYQgQAAAAGAAAAAAAAADsAAADMAAAAf27MABiJIQOCbswA_4ghAwAAXwVs52D3", + "name": "Torstraße" + }, + { + "location": [ + 13.428554, + 52.523239 + ], + "hint": "7UcAgP___38fAAAAUQAAACYAAABTAAAAhSQKQrXq5kKRbiZCWJo_Qx8AAABRAAAAJgAAAFMAAADMAAAASufMAOdwIQNL58wA03AhAwMAvxBs52D3", + "name": "Platz der Vereinten Nationen" + } + ], + "code": "Ok", + "distances": [ + [ + 0, + 1886.89, + 3791.3 + ], + [ + 1824, + 0, + 2838.09 + ], + [ + 3275.36, + 2361.73, + 0 + ] + ] +} +``` + + ### Match service Map matching matches/snaps given GPS points to the road network in the most plausible way. diff --git a/docs/nodejs/api.md b/docs/nodejs/api.md index eec9ab67de4..01ef653cb43 100644 --- a/docs/nodejs/api.md +++ b/docs/nodejs/api.md @@ -110,8 +110,8 @@ Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refer ### table -Computes duration tables for the given locations. Allows for both symmetric and asymmetric -tables. +Computes duration table for the given locations. Allows for both symmetric and asymmetric +tables. Optionally returns distance table. **Parameters** diff --git a/docs/profiles.md b/docs/profiles.md index c539d5121e8..248e9cb526c 100644 --- a/docs/profiles.md +++ b/docs/profiles.md @@ -68,7 +68,7 @@ If you want to prioritize certain streets, increase the rate on these. ## Elements ### api_version -A profile should set `api_version` at the top of your profile. This is done to ensure that older profiles are still supported when the api changes. If `api_version` is not defined, 0 will be assumed. The current api version is 2. +A profile should set `api_version` at the top of your profile. This is done to ensure that older profiles are still supported when the api changes. If `api_version` is not defined, 0 will be assumed. The current api version is 4. ### Library files The folder [profiles/lib/](../profiles/lib/) contains LUA library files for handling many common processing tasks. @@ -224,8 +224,8 @@ source_highway_turn_classification | Read | Integer | source_access_turn_classification | Read | Integer | Classification based on access tag defined by user during setup. (default when not set: 0, allowed classification values are: 0-15)) source_speed | Read | Integer | Speed on this source road in km/h source_priority_class | Read | Enum | The type of road priority class of the source. Defined in `include/extractor/guidance/road_classification.hpp` -target_restricted | Read | Boolean | Is it from a restricted access road? (See definition in `process_way`) -target_mode | Read | Enum | Travel mode before the turn. Defined in `include/extractor/travel_mode.hpp` +target_restricted | Read | Boolean | Is the target a restricted access road? (See definition in `process_way`) +target_mode | Read | Enum | Travel mode after the turn. Defined in `include/extractor/travel_mode.hpp` target_is_motorway | Read | Boolean | Is the target road a motorway? target_is_link | Read | Boolean | Is the target road a link? target_number_of_lanes | Read | Integer | How many lanes does the target road have? (default when not tagged: 0) diff --git a/docs/releasing.md b/docs/releasing.md index efe78dddeed..9a492cd495f 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -43,9 +43,9 @@ We may introduce forward-compatible changes: query parameters and response prope 1. Check out the appropriate release branch `x.y` 2. Make sure `CHANGELOG.md` is up to date. -3. Make sure the `package.json` is up to date. -4. Make sure all tests are passing (e.g. Travis CI gives you a :thumbs_up:) -5. Use an annotated tag to mark the release: `git tag vx.y.z -a` Body of the tag description should be the changelog entries. +3. Make sure the `package.json` on branch `x.y` has been committed. +4. Make sure all tests are passing (e.g. Travis CI gives you a :green_apple:) +5. Use an annotated tag to mark the release: `git tag vx.y.z -a` Body of the tag description should be the changelog entries. Commit should be one in which the `package.json` version matches the version you want to release. 6. Use `npm run docs` to generate the API documentation. Copy `build/docs/*` to `https://github.com/Project-OSRM/project-osrm.github.com` in the `docs/vN.N.N/api` directory 7. Push tags and commits: `git push; git push --tags` 8. On https://github.com/Project-OSRM/osrm-backend/releases press `Draft a new release`, diff --git a/features/bicycle/classes.feature b/features/bicycle/classes.feature new file mode 100644 index 00000000000..170abc17283 --- /dev/null +++ b/features/bicycle/classes.feature @@ -0,0 +1,92 @@ +@routing @bicycle @mode +Feature: Bicycle - Mode flag + Background: + Given the profile "bicycle" + + Scenario: Bicycle - We tag ferries with a class + Given the node map + """ + a b + c d + """ + + And the ways + | nodes | highway | route | + | ab | primary | | + | bc | | ferry | + | cd | primary | | + + When I route I should get + | from | to | route | turns | classes | + | a | d | ab,bc,cd,cd | depart,notification right,notification left,arrive | [()],[(ferry)],[()],[()] | + | d | a | cd,bc,ab,ab | depart,notification right,notification left,arrive | [()],[(ferry)],[()],[()] | + | c | a | bc,ab,ab | depart,notification left,arrive | [(ferry)],[()],[()] | + | d | b | cd,bc,bc | depart,notification right,arrive | [()],[(ferry)],[()] | + | a | c | ab,bc,bc | depart,notification right,arrive | [()],[(ferry)],[()] | + | b | d | bc,cd,cd | depart,notification left,arrive | [(ferry)],[()],[()] | + + Scenario: Bicycle - We tag tunnel with a class + Background: + Given a grid size of 200 meters + + Given the node map + """ + a b + c d + """ + + And the ways + | nodes | tunnel | + | ab | no | + | bc | yes | + | cd | | + + When I route I should get + | from | to | route | turns | classes | + | a | d | ab,bc,cd,cd | depart,new name right,new name left,arrive | [()],[(tunnel)],[()],[()] | + + Scenario: Bicycle - We tag classes without intersections + Background: + Given a grid size of 200 meters + + Given the node map + """ + a b c d + """ + + And the ways + | nodes | name | tunnel | + | ab | road | | + | bc | road | yes | + | cd | road | | + + When I route I should get + | from | to | route | turns | classes | + | a | d | road,road | depart,arrive | [(),(tunnel),()],[()] | + + Scenario: Bicycle - From roundabout on ferry + Given the node map + """ + c + / \ + a---b d---f--h + \ / + e + | + g + """ + + And the ways + | nodes | oneway | highway | junction | route | + | ab | yes | service | | | + | cb | yes | service | roundabout | | + | dc | yes | service | roundabout | | + | be | yes | service | roundabout | | + | ed | yes | service | roundabout | | + | eg | yes | service | | | + | df | | | | ferry | + | fh | yes | service | | | + + When I route I should get + | from | to | route | turns | classes | + | a | h | ab,df,df,fh,fh | depart,roundabout-exit-2,exit roundabout slight right,notification straight,arrive | [()],[(),()],[(ferry)],[()],[()] | diff --git a/features/bicycle/exclude.feature b/features/bicycle/exclude.feature new file mode 100644 index 00000000000..6fdcbfb0737 --- /dev/null +++ b/features/bicycle/exclude.feature @@ -0,0 +1,55 @@ +@routing @bicycle @exclude +Feature: Bicycle - Exclude flags + Background: + Given the profile file "bicycle" initialized with + """ + profile.excludable = Sequence { Set { 'ferry' } } + """ + Given the node map + """ + a....b~~~~~c...f + : : + d.....e + """ + + And the ways + | nodes | highway | route | duration | # | + | ab | service | | | always drivable | + | bc | | ferry | 00:00:01 | not drivable for exclude=ferry, but fast. | + | bd | service | | | always drivable | + | de | service | | | always drivable | + | ec | service | | | always drivable | + | cf | service | | | always drivable | + + Scenario: Bicycle - exclude nothing + When I route I should get + | from | to | route | + | a | f | ab,bc,cf,cf | + + When I match I should get + | trace | matchings | duration | + | abcf | abcf | 109 | + + When I request a travel time matrix I should get + | | a | f | + | a | 0 | 109 | + | f | 109 | 0 | + + Scenario: Bicycle - exclude ferry + Given the query options + | exclude | ferry | + + When I route I should get + | from | to | route | + | a | f | ab,bd,de,ec,cf,cf | + + When I match I should get + | trace | matchings | duration | + | abcf | abcf | 301.2 | + + When I request a travel time matrix I should get + | | a | f | + | a | 0 | 301 +- 1 | + | f | 301.2 +- 1 | 0 | + + diff --git a/features/guidance/obvious-turn-discovery.feature b/features/guidance/obvious-turn-discovery.feature index 414894e7cbb..fd846aceff2 100644 --- a/features/guidance/obvious-turn-discovery.feature +++ b/features/guidance/obvious-turn-discovery.feature @@ -1209,3 +1209,33 @@ Feature: Simple Turns | a | c | knob,knob | depart,arrive | | d | e | soph,soph | depart,arrive | | d | a | soph,knob,knob | depart,turn left,arrive | + + + # https://www.openstreetmap.org/node/30797565 + Scenario: No turn instruction when turning from unnamed onto unnamed + Given the node map + """ + a + | + | + | + | + b----------------c + | + | + | + | + | + | + d + """ + + And the ways + | nodes | highway | name | ref | + | ab | trunk_link | | | + | db | secondary | | L 460 | + | bc | secondary | | | + + When I route I should get + | from | to | route | turns | + | d | c | ,, | depart,turn right,arrive | diff --git a/features/step_definitions/distance_matrix.js b/features/step_definitions/distance_matrix.js index aab1f606458..fde80679f5f 100644 --- a/features/step_definitions/distance_matrix.js +++ b/features/step_definitions/distance_matrix.js @@ -1,74 +1,88 @@ var util = require('util'); module.exports = function () { - this.When(/^I request a travel time matrix I should get$/, (table, callback) => { - var NO_ROUTE = 2147483647; // MAX_INT + const durationsRegex = new RegExp(/^I request a travel time matrix I should get$/); + const distancesRegex = new RegExp(/^I request a travel distance matrix I should get$/); - var tableRows = table.raw(); + const DURATIONS_NO_ROUTE = 2147483647; // MAX_INT + const DISTANCES_NO_ROUTE = 3.40282e+38; // MAX_FLOAT - if (tableRows[0][0] !== '') throw new Error('*** Top-left cell of matrix table must be empty'); + this.When(durationsRegex, function(table, callback) {tableParse.call(this, table, DURATIONS_NO_ROUTE, 'durations', callback);}.bind(this)); + this.When(distancesRegex, function(table, callback) {tableParse.call(this, table, DISTANCES_NO_ROUTE, 'distances', callback);}.bind(this)); +}; - var waypoints = [], - columnHeaders = tableRows[0].slice(1), - rowHeaders = tableRows.map((h) => h[0]).slice(1), - symmetric = columnHeaders.length == rowHeaders.length && columnHeaders.every((ele, i) => ele === rowHeaders[i]); +const durationsParse = function(v) { return isNaN(parseInt(v)); }; +const distancesParse = function(v) { return isNaN(parseFloat(v)); }; - if (symmetric) { - columnHeaders.forEach((nodeName) => { - var node = this.findNodeByName(nodeName); - if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName)); - waypoints.push({ coord: node, type: 'loc' }); - }); - } else { - columnHeaders.forEach((nodeName) => { - var node = this.findNodeByName(nodeName); - if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName)); - waypoints.push({ coord: node, type: 'dst' }); - }); - rowHeaders.forEach((nodeName) => { - var node = this.findNodeByName(nodeName); - if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName)); - waypoints.push({ coord: node, type: 'src' }); +function tableParse(table, noRoute, annotation, callback) { + + const parse = annotation == 'distances' ? distancesParse : durationsParse; + const params = this.queryParams; + params.annotations = annotation == 'distances' ? 'distance' : 'duration'; + + var tableRows = table.raw(); + + if (tableRows[0][0] !== '') throw new Error('*** Top-left cell of matrix table must be empty'); + + var waypoints = [], + columnHeaders = tableRows[0].slice(1), + rowHeaders = tableRows.map((h) => h[0]).slice(1), + symmetric = columnHeaders.length == rowHeaders.length && columnHeaders.every((ele, i) => ele === rowHeaders[i]); + + if (symmetric) { + columnHeaders.forEach((nodeName) => { + var node = this.findNodeByName(nodeName); + if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName)); + waypoints.push({ coord: node, type: 'loc' }); + }); + } else { + columnHeaders.forEach((nodeName) => { + var node = this.findNodeByName(nodeName); + if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName)); + waypoints.push({ coord: node, type: 'dst' }); + }); + rowHeaders.forEach((nodeName) => { + var node = this.findNodeByName(nodeName); + if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName)); + waypoints.push({ coord: node, type: 'src' }); + }); + } + + var actual = []; + actual.push(table.headers); + + this.reprocessAndLoadData((e) => { + if (e) return callback(e); + // compute matrix + + this.requestTable(waypoints, params, (err, response) => { + if (err) return callback(err); + if (!response.body.length) return callback(new Error('Invalid response body')); + + var json = JSON.parse(response.body); + + var result = json[annotation].map(row => { + var hashes = {}; + row.forEach((v, i) => { hashes[tableRows[0][i+1]] = parse(v) ? '' : v; }); + return hashes; }); - } - - var actual = []; - actual.push(table.headers); - - this.reprocessAndLoadData((e) => { - if (e) return callback(e); - // compute matrix - var params = this.queryParams; - - this.requestTable(waypoints, params, (err, response) => { - if (err) return callback(err); - if (!response.body.length) return callback(new Error('Invalid response body')); - - var json = JSON.parse(response.body); - - var result = json['durations'].map(row => { - var hashes = {}; - row.forEach((v, i) => { hashes[tableRows[0][i+1]] = isNaN(parseInt(v)) ? '' : v; }); - return hashes; - }); - - var testRow = (row, ri, cb) => { - for (var k in result[ri]) { - if (this.FuzzyMatch.match(result[ri][k], row[k])) { - result[ri][k] = row[k]; - } else if (row[k] === '' && result[ri][k] === NO_ROUTE) { - result[ri][k] = ''; - } else { - result[ri][k] = result[ri][k].toString(); - } + + var testRow = (row, ri, cb) => { + for (var k in result[ri]) { + if (this.FuzzyMatch.match(result[ri][k], row[k])) { + result[ri][k] = row[k]; + } else if (row[k] === '' && result[ri][k] === noRoute) { + result[ri][k] = ''; + } else { + result[ri][k] = result[ri][k].toString(); } + } - result[ri][''] = row['']; - cb(null, result[ri]); - }; + result[ri][''] = row['']; + cb(null, result[ri]); + }; - this.processRowsAndDiff(table, testRow, callback); - }); + this.processRowsAndDiff(table, testRow, callback); }); }); -}; +} diff --git a/features/support/data_classes.js b/features/support/data_classes.js index a383540d6e0..e276f20edb5 100644 --- a/features/support/data_classes.js +++ b/features/support/data_classes.js @@ -21,7 +21,8 @@ module.exports = { matchRe = want.match(/^\/(.*)\/$/), // we use this for matching before/after bearing matchBearingListAbs = want.match(/^((\d+)->(\d+))(,(\d+)->(\d+))*\s+\+\-(.+)$/), - matchIntersectionListAbs = want.match(/^(((((true|false):\d+)\s{0,1})+,{0,1})+;{0,1})+\s+\+\-(.+)$/); + matchIntersectionListAbs = want.match(/^(((((true|false):\d+)\s{0,1})+,{0,1})+;{0,1})+\s+\+\-(.+)$/), + matchRangeNumbers = want.match(/\d+\+\-\d+/); function inRange(margin, got, want) { var fromR = parseFloat(want) - margin, @@ -105,6 +106,11 @@ module.exports = { return inRange(margin, got, matchAbs[1]); } else if (matchRe) { // regex: /a,b,.*/ return got.match(matchRe[1]); + } else if (matchRangeNumbers) { + let real_want_and_margin = want.split('+-'), + margin = parseFloat(real_want_and_margin[1].trim()), + real_want = parseFloat(real_want_and_margin[0].trim()); + return inRange(margin, got, real_want); } else { return false; } diff --git a/features/support/hooks.js b/features/support/hooks.js index e58b34bfa22..01c2e6e8921 100644 --- a/features/support/hooks.js +++ b/features/support/hooks.js @@ -51,7 +51,7 @@ module.exports = function () { .defer(rimraf, this.scenarioLogFile) .awaitAll(callback); // uncomment to get path to logfile - // console.log(" Writing logging output to " + this.scenarioLogFile) + // console.log(' Writing logging output to ' + this.scenarioLogFile); }); this.After((scenario, callback) => { diff --git a/features/testbot/distance.feature b/features/testbot/distance.feature index 8500c1df13a..1b76f45a653 100644 --- a/features/testbot/distance.feature +++ b/features/testbot/distance.feature @@ -226,3 +226,22 @@ Feature: Distance calculation | x | v | xv,xv | 424m +-1 | | x | w | xw,xw | 360m +-1 | | x | y | xy,xy | 316m +-1 | + + + # Check rounding errors + Scenario: Distances Long distances + Given a grid size of 1000 meters + Given the node map + """ + a b c d + """ + + And the ways + | nodes | + | abcd | + + When I route I should get + | from | to | distance | + | a | b | 1000m +-3 | + | a | c | 2000m +-3 | + | a | d | 3000m +-3 | diff --git a/features/testbot/distance_matrix.feature b/features/testbot/distance_matrix.feature index 18b400514ef..d97f50924ee 100644 --- a/features/testbot/distance_matrix.feature +++ b/features/testbot/distance_matrix.feature @@ -1,14 +1,12 @@ @matrix @testbot Feature: Basic Distance Matrix -# note that results are travel time, specified in 1/10th of seconds -# since testbot uses a default speed of 100m/10s, the result matches -# the number of meters as long as the way type is the default 'primary' +# note that results of travel distance are in metres Background: Given the profile "testbot" And the partition extra arguments "--small-component-size 1 --max-cell-sizes 2,4,8,16" - Scenario: Testbot - Travel time matrix of minimal network + Scenario: Testbot - Travel distance matrix of minimal network Given the node map """ a b @@ -18,57 +16,129 @@ Feature: Basic Distance Matrix | nodes | | ab | - When I request a travel time matrix I should get - | | a | b | - | a | 0 | 10 | - | b | 10 | 0 | + When I request a travel distance matrix I should get + | | a | b | + | a | 0 | 100+-1 | + | b | 100+-1 | 0 | + + Scenario: Testbot - Travel distance matrix of minimal network with toll exclude + Given the query options + | exclude | toll | - Scenario: Testbot - Travel time matrix with different way speeds Given the node map """ - a b c d + a b + c d """ And the ways - | nodes | highway | - | ab | primary | - | bc | secondary | - | cd | tertiary | + | nodes | highway | toll | # | + | ab | motorway | | not drivable for exclude=motorway | + | cd | primary | | always drivable | + | ac | primary | yes | not drivable for exclude=toll and exclude=motorway,toll | + | bd | motorway | yes | not drivable for exclude=toll and exclude=motorway,toll | + + When I request a travel distance matrix I should get + | | a | b | c | d | + | a | 0 | 100+-1 | | | + | b | 100+-1 | 0 | | | + | c | | | 0 | 100+-1 | + | d | | | 100+-1 | 0 | + + Scenario: Testbot - Travel distance matrix of minimal network with motorway exclude + Given the query options + | exclude | motorway | - When I request a travel time matrix I should get - | | a | b | c | d | - | a | 0 | 10 | 30 | 60 | - | b | 10 | 0 | 20 | 50 | - | c | 30 | 20 | 0 | 30 | - | d | 60 | 50 | 30 | 0 | - - When I request a travel time matrix I should get - | | a | b | c | d | - | a | 0 | 10 | 30 | 60 | - - When I request a travel time matrix I should get - | | a | - | a | 0 | - | b | 10 | - | c | 30 | - | d | 60 | - - Scenario: Testbot - Travel time matrix with fuzzy match Given the node map """ a b + c d """ And the ways - | nodes | - | ab | + | nodes | highway | # | + | ab | motorway | not drivable for exclude=motorway | + | cd | residential | | + | ac | residential | | + | bd | residential | | + + When I request a travel distance matrix I should get + | | a | b | c | d | + | a | 0 | 300+-2 | 100+-2 | 200+-2 | + + Scenario: Testbot - Travel distance matrix of minimal network disconnected motorway exclude + Given the query options + | exclude | motorway | + And the extract extra arguments "--small-component-size 4" + + Given the node map + """ + ab efgh + cd + """ + + And the ways + | nodes | highway | # | + | be | motorway | not drivable for exclude=motorway | + | abcd | residential | | + | efgh | residential | | + + When I request a travel distance matrix I should get + | | a | b | e | + | a | 0 | 50+-1 | | + + Scenario: Testbot - Travel distance matrix of minimal network with motorway and toll excludes + Given the query options + | exclude | motorway,toll | + + Given the node map + """ + a b e f + c d g h + """ + + And the ways + | nodes | highway | toll | # | + | be | motorway | | not drivable for exclude=motorway | + | dg | primary | yes | not drivable for exclude=toll | + | abcd | residential | | | + | efgh | residential | | | - When I request a travel time matrix I should get - | | a | b | - | a | 0 | 10 | - | b | 10 | 0 | + When I request a travel distance matrix I should get + | | a | b | e | g | + | a | 0 | 100+-1 | | | + + Scenario: Testbot - Travel distance matrix with different way speeds + Given the node map + """ + a b c d + """ + + And the ways + | nodes | highway | + | ab | primary | + | bc | secondary | + | cd | tertiary | - Scenario: Testbot - Travel time matrix of small grid + When I request a travel distance matrix I should get + | | a | b | c | d | + | a | 0 | 100+-1 | 200+-1 | 300+-1 | + | b | 100+-1 | 0 | 100+-1 | 200+-1 | + | c | 200+-1 | 100+-1 | 0 | 100+-1 | + | d | 300+-1 | 200+-1 | 100+-1 | 0 | + + When I request a travel distance matrix I should get + | | a | b | c | d | + | a | 0 | 100+-1 | 200+-1 | 300+-1 | + + When I request a travel distance matrix I should get + | | a | + | a | 0 | + | b | 100+-1 | + | c | 200+-1 | + | d | 300+-1 | + + Scenario: Testbot - Travel distance matrix of small grid Given the node map """ a b c @@ -83,14 +153,14 @@ Feature: Basic Distance Matrix | be | | cf | - When I request a travel time matrix I should get - | | a | b | e | f | - | a | 0 | 10 | 20 | 30 | - | b | 10 | 0 | 10 | 20 | - | e | 20 | 10 | 0 | 10 | - | f | 30 | 20 | 10 | 0 | + When I request a travel distance matrix I should get + | | a | b | e | f | + | a | 0 | 100+-1 | 200+-1 | 300+-1 | + | b | 100+-1 | 0 | 100+-1 | 200+-1 | + | e | 200+-1 | 100+-1 | 0 | 100+-1 | + | f | 300+-1 | 200+-1 | 100+-1 | 0 | - Scenario: Testbot - Travel time matrix of network with unroutable parts + Scenario: Testbot - Travel distance matrix of network with unroutable parts Given the node map """ a b @@ -100,12 +170,12 @@ Feature: Basic Distance Matrix | nodes | oneway | | ab | yes | - When I request a travel time matrix I should get - | | a | b | - | a | 0 | 10 | - | b | | 0 | + When I request a travel distance matrix I should get + | | a | b | + | a | 0 | 100+-1 | + | b | | 0 | - Scenario: Testbot - Travel time matrix of network with oneways + Scenario: Testbot - Travel distance matrix of network with oneways Given the node map """ x a b y @@ -118,14 +188,14 @@ Feature: Basic Distance Matrix | xa | | | by | | - When I request a travel time matrix I should get - | | x | y | d | e | - | x | 0 | 30 | 40 | 30 | - | y | 50 | 0 | 30 | 20 | - | d | 20 | 30 | 0 | 30 | - | e | 30 | 40 | 10 | 0 | + When I request a travel distance matrix I should get + | | x | y | d | e | + | x | 0 | 300+-2 | 400+-2 | 300+-2 | + | y | 500+-2 | 0 | 300+-2 | 200+-2 | + | d | 200+-2 | 300+-2 | 0 | 300+-2 | + | e | 300+-2 | 400+-2 | 100+-2 | 0 | - Scenario: Testbot - Rectangular travel time matrix + Scenario: Testbot - Rectangular travel distance matrix Given the node map """ a b c @@ -140,51 +210,57 @@ Feature: Basic Distance Matrix | be | | cf | - When I request a travel time matrix I should get - | | a | b | e | f | - | a | 0 | 10 | 20 | 30 | - - When I request a travel time matrix I should get - | | a | - | a | 0 | - | b | 10 | - | e | 20 | - | f | 30 | - - When I request a travel time matrix I should get - | | a | b | e | f | - | a | 0 | 10 | 20 | 30 | - | b | 10 | 0 | 10 | 20 | - - When I request a travel time matrix I should get - | | a | b | - | a | 0 | 10 | - | b | 10 | 0 | - | e | 20 | 10 | - | f | 30 | 20 | - - When I request a travel time matrix I should get - | | a | b | e | f | - | a | 0 | 10 | 20 | 30 | - | b | 10 | 0 | 10 | 20 | - | e | 20 | 10 | 0 | 10 | - - When I request a travel time matrix I should get - | | a | b | e | - | a | 0 | 10 | 20 | - | b | 10 | 0 | 10 | - | e | 20 | 10 | 0 | - | f | 30 | 20 | 10 | - - When I request a travel time matrix I should get - | | a | b | e | f | - | a | 0 | 10 | 20 | 30 | - | b | 10 | 0 | 10 | 20 | - | e | 20 | 10 | 0 | 10 | - | f | 30 | 20 | 10 | 0 | - - - Scenario: Testbot - Travel time 3x2 matrix + When I route I should get + | from | to | distance | + | e | a | 200m +- 1 | + | e | b | 100m +- 1 | + | f | a | 300m +- 1 | + | f | b | 200m +- 1 | + + When I request a travel distance matrix I should get + | | a | b | e | f | + | a | 0 | 100+-1 | 200+-1 | 300+-1 | + + When I request a travel distance matrix I should get + | | a | + | a | 0 | + | b | 100+-1 | + | e | 200+-1 | + | f | 300+-1 | + + When I request a travel distance matrix I should get + | | a | b | e | f | + | a | 0 | 100+-1 | 200+-1 | 300+-1 | + | b | 100+-1 | 0 | 100+-1 | 200+-1 | + + When I request a travel distance matrix I should get + | | a | b | + | a | 0 | 100+-1 | + | b | 100+-1 | 0 | + | e | 200+-1 | 100+-1 | + | f | 300+-1 | 200+-1 | + + When I request a travel distance matrix I should get + | | a | b | e | f | + | a | 0 | 100+-1 | 200+-1 | 300+-1 | + | b | 100+-1 | 0 | 100+-1 | 200+-1 | + | e | 200+-1 | 100+-1 | 0 | 100+-1 | + + When I request a travel distance matrix I should get + | | a | b | e | + | a | 0 | 100+-1 | 200+-1 | + | b | 100+-1 | 0 | 100+-1 | + | e | 200+-1 | 100+-1 | 0 | + | f | 300+-1 | 200+-1 | 100+-1 | + + When I request a travel distance matrix I should get + | | a | b | e | f | + | a | 0 | 100+-1 | 200+-1 | 300+-1 | + | b | 100+-1 | 0 | 100+-1 | 200+-1 | + | e | 200+-1 | 100+-1 | 0 | 100+-1 | + | f | 300+-1 | 200+-1 | 100+-1 | 0 | + + Scenario: Testbot - Travel distance 3x2 matrix Given the node map """ a b c @@ -199,10 +275,11 @@ Feature: Basic Distance Matrix | be | | cf | - When I request a travel time matrix I should get - | | b | e | f | - | a | 10 | 20 | 30 | - | b | 0 | 10 | 20 | + + When I request a travel distance matrix I should get + | | b | e | f | + | a | 100+-1 | 200+-1 | 300+-1 | + | b | 0 | 100+-1 | 200+-1 | Scenario: Testbot - All coordinates are from same small component Given a grid size of 300 meters @@ -221,10 +298,10 @@ Feature: Basic Distance Matrix | da | | fg | - When I request a travel time matrix I should get - | | f | g | - | f | 0 | 30 | - | g | 30 | 0 | + When I request a travel distance matrix I should get + | | f | g | + | f | 0 | 300+-2 | + | g | 300+-2 | 0 | Scenario: Testbot - Coordinates are from different small component and snap to big CC Given a grid size of 300 meters @@ -244,14 +321,25 @@ Feature: Basic Distance Matrix | fg | | hi | - When I request a travel time matrix I should get - | | f | g | h | i | - | f | 0 | 30 | 0 | 30 | - | g | 30 | 0 | 30 | 0 | - | h | 0 | 30 | 0 | 30 | - | i | 30 | 0 | 30 | 0 | - - Scenario: Testbot - Travel time matrix with loops + When I route I should get + | from | to | distance | + | f | g | 300m | + | f | i | 300m | + | g | f | 300m | + | g | h | 300m | + | h | g | 300m | + | h | i | 300m | + | i | f | 300m | + | i | h | 300m | + + When I request a travel distance matrix I should get + | | f | g | h | i | + | f | 0 | 300+-2 | 0 | 300+-2 | + | g | 300+-2 | 0 | 300+-2 | 0 | + | h | 0 | 300+-2 | 0 | 300+-2 | + | i | 300+-2 | 0 | 300+-2 | 0 | + + Scenario: Testbot - Travel distance matrix with loops Given the node map """ a 1 2 b @@ -265,14 +353,15 @@ Feature: Basic Distance Matrix | cd | yes | | da | yes | - When I request a travel time matrix I should get - | | 1 | 2 | 3 | 4 | - | 1 | 0 | 10 +-1 | 40 +-1 | 50 +-1 | - | 2 | 70 +-1 | 0 | 30 +-1 | 40 +-1 | - | 3 | 40 +-1 | 50 +-1 | 0 | 10 +-1 | - | 4 | 30 +-1 | 40 +-1 | 70 +-1 | 0 | + When I request a travel distance matrix I should get + | | 1 | 2 | 3 | 4 | + | 1 | 0 | 100+-1 | 400+-1 | 500+-1 | + | 2 | 700+-1 | 0 | 300+-1 | 400+-1 | + | 3 | 400+-1 | 500+-1 | 0 | 100+-1 | + | 4 | 300+-1 | 400+-1 | 700+-1 | 0 | + - Scenario: Testbot - Travel time matrix based on segment durations + Scenario: Testbot - Travel distance matrix based on segment durations Given the profile file """ local functions = require('testbot') @@ -301,20 +390,19 @@ Feature: Basic Distance Matrix """ And the ways - | nodes | - | abcd | - | ce | - - When I request a travel time matrix I should get - | | a | b | c | d | e | - | a | 0 | 11 | 22 | 33 | 33 | - | b | 11 | 0 | 11 | 22 | 22 | - | c | 22 | 11 | 0 | 11 | 11 | - | d | 33 | 22 | 11 | 0 | 22 | - | e | 33 | 22 | 11 | 22 | 0 | - - - Scenario: Testbot - Travel time matrix for alternative loop paths + | nodes | + | abcd | + | ce | + + When I request a travel distance matrix I should get + | | a | b | c | d | e | + | a | 0 | 100+-2 | 200+-2 | 300+-2 | 400+-2 | + | b | 100+-2 | 0 | 100+-2 | 200+-2 | 300+-2 | + | c | 200+-2 | 100+-2 | 0 | 100+-2 | 200+-2 | + | d | 300+-2 | 200+-2 | 100+-2 | 0 | 300+-2 | + | e | 400+-2 | 300+-2 | 200+-2 | 300+-2 | 0 | + + Scenario: Testbot - Travel distance matrix for alternative loop paths Given the profile file """ local functions = require('testbot') @@ -350,62 +438,132 @@ Feature: Basic Distance Matrix | dc | yes | | ca | yes | - When I request a travel time matrix I should get - | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | - | 1 | 0 | 11 | 3 | 2 | 6 | 5 | 8.9 | 7.9 | - | 2 | 1 | 0 | 4 | 3 | 7 | 6 | 9.9 | 8.9 | - | 3 | 9 | 8 | 0 | 11 | 3 | 2 | 5.9 | 4.9 | - | 4 | 10 | 9 | 1 | 0 | 4 | 3 | 6.9 | 5.9 | - | 5 | 6 | 5 | 9 | 8 | 0 | 11 | 2.9 | 1.9 | - | 6 | 7 | 6 | 10 | 9 | 1 | 0 | 3.9 | 2.9 | - | 7 | 3.1 | 2.1 | 6.1 | 5.1 | 9.1 | 8.1 | 0 | 11 | - | 8 | 4.1 | 3.1 | 7.1 | 6.1 | 10.1 | 9.1 | 1 | 0 | - - - Scenario: Testbot - Travel time matrix with ties - Given the profile file - """ - local functions = require('testbot') - functions.process_segment = function(profile, segment) - segment.weight = 1 - segment.duration = 1 - end - functions.process_turn = function(profile, turn) - if turn.angle >= 0 then - turn.duration = 16 - else - turn.duration = 4 - end - turn.weight = 0 - end - return functions - """ - And the node map + When I request a travel distance matrix I should get + | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | + | 1 | 0 | 1100+-5 | 300+-5 | 200+-5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 | + | 2 | 100+-5 | 0 | 400+-5 | 300+-5 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 | + | 3 | 900+-5 | 800+-5 | 0 | 1100+-5 | 300+-5 | 200+-5 | 600+-5 | 500+-5 | + | 4 | 1000+-5 | 900+-5 | 100+-5 | 0 | 400+-5 | 300+-5 | 700+-5 | 600+-5 | + | 5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 | 0 | 1100+-5 | 300+-5 | 200+-5 | + | 6 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 | 100+-5 | 0 | 400+-5 | 300+-5 | + | 7 | 300+-5 | 200+-5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 | 0 | 1100+-5 | + | 8 | 400+-5 | 300+-5 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 | 100+-5 | 0 | + + When I request a travel distance matrix I should get + | | 1 | + | 1 | 0 | + | 2 | 100+-5 | + | 3 | 900+-5 | + | 4 | 1000+-5 | + | 5 | 600+-5 | + | 6 | 700+-5 | + | 7 | 300+-5 | + | 8 | 400+-5 | + + Scenario: Testbot - Travel distance matrix with ties + Given the node map """ - a b + a b c d """ And the ways - | nodes | - | ab | - | ac | - | bd | - | dc | + | nodes | + | ab | + | ac | + | bd | + | dc | + When I route I should get + | from | to | route | distance | time | weight | + | a | c | ac,ac | 200m | 20s | 20 | When I route I should get - | from | to | route | distance | time | weight | - | a | c | ac,ac | 200m | 5s | 5 | - - When I request a travel time matrix I should get - | | a | b | c | d | - | a | 0 | 1 | 5 | 10 | - - When I request a travel time matrix I should get - | | a | - | a | 0 | - | b | 1 | - | c | 15 | - | d | 10 | + | from | to | route | distance | + | a | b | ab,ab | 450m | + | a | c | ac,ac | 200m | + | a | d | ac,dc,dc | 500m +- 1 | + + When I request a travel distance matrix I should get + | | a | b | c | d | + | a | 0 | 450+-2 | 200+-2 | 500+-2 | + + When I request a travel distance matrix I should get + | | a | + | a | 0 | + | b | 450+-2 | + | c | 200+-2 | + | d | 500+-2 | + + When I request a travel distance matrix I should get + | | a | c | + | a | 0 | 200+-2 | + | c | 200+-2 | 0 | + + + # Check rounding errors + Scenario: Testbot - Long distances in tables + Given a grid size of 1000 meters + Given the node map + """ + a b c d + """ + + And the ways + | nodes | + | abcd | + + When I request a travel distance matrix I should get + | | a | b | c | d | + | a | 0 | 1000+-3 | 2000+-3 | 3000+-3 | + + + Scenario: Testbot - OneToMany vs ManyToOne + Given the node map + """ + a b + c + """ + + And the ways + | nodes | oneway | + | ab | yes | + | ac | | + | bc | | + + When I request a travel distance matrix I should get + | | a | b | + | b | 240.4 | 0 | + + When I request a travel distance matrix I should get + | | a | + | a | 0 | + | b | 240.4 | + + Scenario: Testbot - Varying distances between nodes + Given the node map + """ + a b c d + + e + + + + f + """ + + And the ways + | nodes | oneway | + | feabcd | yes | + | ec | | + | fd | | + + When I request a travel distance matrix I should get + | | a | b | c | d | e | f | + | a | 0 | 100+-1 | 300+-1 | 650+-1 | 1930+-1 | 1533+-1 | + | b | 760+-1 | 0 | 200+-1 | 550+-1 | 1830+-1 | 1433+-1 | + | c | 560+-2 | 660+-2 | 0 | 350+-1 | 1630+-1 | 1233+-1 | + | d | 1480+-2 | 1580+-1 | 1780+-1 | 0 | 1280+-1 | 883+-1 | + | e | 200+-2 | 300+-2 | 500+-1 | 710+-1 | 0 | 1593+-1 | + | f | 597+-1 | 696+-1 | 896+-1 | 1108+-1 | 400+-3 | 0 | diff --git a/features/testbot/duration_matrix.feature b/features/testbot/duration_matrix.feature new file mode 100644 index 00000000000..c6977dfabd9 --- /dev/null +++ b/features/testbot/duration_matrix.feature @@ -0,0 +1,512 @@ +@matrix @testbot +Feature: Basic Duration Matrix +# note that results of travel time are in seconds + + Background: + Given the profile "testbot" + And the partition extra arguments "--small-component-size 1 --max-cell-sizes 2,4,8,16" + + Scenario: Testbot - Travel time matrix of minimal network + Given the node map + """ + a b + """ + + And the ways + | nodes | + | ab | + + When I request a travel time matrix I should get + | | a | b | + | a | 0 | 10 | + | b | 10 | 0 | + + @ch + Scenario: Testbot - Travel time matrix of minimal network with toll exclude + Given the query options + | exclude | toll | + + Given the node map + """ + a b + c d + """ + + And the ways + | nodes | highway | toll | # | + | ab | motorway | | not drivable for exclude=motorway | + | cd | primary | | always drivable | + | ac | motorway | yes | not drivable for exclude=toll and exclude=motorway,toll | + | bd | motorway | yes | not drivable for exclude=toll and exclude=motorway,toll | + + When I request a travel time matrix I should get + | | a | b | c | d | + | a | 0 | 15 | | | + | b | 15 | 0 | | | + | c | | | 0 | 10 | + | d | | | 10 | 0 | + + @ch + Scenario: Testbot - Travel time matrix of minimal network with motorway exclude + Given the query options + | exclude | motorway | + + Given the node map + """ + a b + c d + """ + + And the ways + | nodes | highway | # | + | ab | motorway | not drivable for exclude=motorway | + | cd | residential | | + | ac | residential | | + | bd | residential | | + + When I request a travel time matrix I should get + | | a | b | c | d | + | a | 0 | 45 | 15 | 30 | + + @ch + Scenario: Testbot - Travel time matrix of minimal network disconnected motorway exclude + Given the query options + | exclude | motorway | + + Given the node map + """ + ab efgh + cd + """ + + And the ways + | nodes | highway | # | + | be | motorway | not drivable for exclude=motorway | + | abcd | residential | | + | efgh | residential | | + + When I request a travel time matrix I should get + | | a | b | e | + | a | 0 | 7.5 | | + + @ch + Scenario: Testbot - Travel time matrix of minimal network with motorway and toll excludes + Given the query options + | exclude | motorway,toll | + + Given the node map + """ + a b e f + c d g h + """ + + And the ways + | nodes | highway | toll | # | + | be | motorway | | not drivable for exclude=motorway | + | dg | primary | yes | not drivable for exclude=toll | + | abcd | residential | | | + | efgh | residential | | | + + When I request a travel time matrix I should get + | | a | b | e | g | + | a | 0 | 15 | | | + + Scenario: Testbot - Travel time matrix with different way speeds + Given the node map + """ + a b c d + """ + + And the ways + | nodes | highway | + | ab | primary | + | bc | secondary | + | cd | tertiary | + + When I request a travel time matrix I should get + | | a | b | c | d | + | a | 0 | 10 | 30 | 60 | + | b | 10 | 0 | 20 | 50 | + | c | 30 | 20 | 0 | 30 | + | d | 60 | 50 | 30 | 0 | + + When I request a travel time matrix I should get + | | a | b | c | d | + | a | 0 | 10 | 30 | 60 | + + When I request a travel time matrix I should get + | | a | + | a | 0 | + | b | 10 | + | c | 30 | + | d | 60 | + + + Scenario: Testbot - Travel time matrix of small grid + Given the node map + """ + a b c + d e f + """ + + And the ways + | nodes | + | abc | + | def | + | ad | + | be | + | cf | + + When I request a travel time matrix I should get + | | a | b | e | f | + | a | 0 | 10 | 20 | 30 | + | b | 10 | 0 | 10 | 20 | + | e | 20 | 10 | 0 | 10 | + | f | 30 | 20 | 10 | 0 | + + + Scenario: Testbot - Travel time matrix of network with unroutable parts + Given the node map + """ + a b + """ + + And the ways + | nodes | oneway | + | ab | yes | + + When I request a travel time matrix I should get + | | a | b | + | a | 0 | 10 | + | b | | 0 | + + + Scenario: Testbot - Travel time matrix of network with oneways + Given the node map + """ + x a b y + d e + """ + + And the ways + | nodes | oneway | + | abeda | yes | + | xa | | + | by | | + + When I request a travel time matrix I should get + | | x | y | d | e | + | x | 0 | 30 | 40 | 30 | + | y | 50 | 0 | 30 | 20 | + | d | 20 | 30 | 0 | 30 | + | e | 30 | 40 | 10 | 0 | + + + Scenario: Testbot - Rectangular travel time matrix + Given the node map + """ + a b c + d e f + """ + + And the ways + | nodes | + | abc | + | def | + | ad | + | be | + | cf | + + When I request a travel time matrix I should get + | | a | b | e | f | + | a | 0 | 10 | 20 | 30 | + + When I request a travel time matrix I should get + | | a | + | a | 0 | + | b | 10 | + | e | 20 | + | f | 30 | + + When I request a travel time matrix I should get + | | a | b | e | f | + | a | 0 | 10 | 20 | 30 | + | b | 10 | 0 | 10 | 20 | + + When I request a travel time matrix I should get + | | a | b | + | a | 0 | 10 | + | b | 10 | 0 | + | e | 20 | 10 | + | f | 30 | 20 | + + When I request a travel time matrix I should get + | | a | b | e | f | + | a | 0 | 10 | 20 | 30 | + | b | 10 | 0 | 10 | 20 | + | e | 20 | 10 | 0 | 10 | + + When I request a travel time matrix I should get + | | a | b | e | + | a | 0 | 10 | 20 | + | b | 10 | 0 | 10 | + | e | 20 | 10 | 0 | + | f | 30 | 20 | 10 | + + When I request a travel time matrix I should get + | | a | b | e | f | + | a | 0 | 10 | 20 | 30 | + | b | 10 | 0 | 10 | 20 | + | e | 20 | 10 | 0 | 10 | + | f | 30 | 20 | 10 | 0 | + + Scenario: Testbot - Travel time 3x2 matrix + Given the node map + """ + a b c + d e f + """ + + And the ways + | nodes | + | abc | + | def | + | ad | + | be | + | cf | + + When I request a travel time matrix I should get + | | b | e | f | + | a | 10 | 20 | 30 | + | b | 0 | 10 | 20 | + + + Scenario: Testbot - All coordinates are from same small component + Given a grid size of 300 meters + Given the extract extra arguments "--small-component-size 4" + Given the node map + """ + a b f + d e g + """ + + And the ways + | nodes | + | ab | + | be | + | ed | + | da | + | fg | + + When I request a travel time matrix I should get + | | f | g | + | f | 0 | 30 | + | g | 30 | 0 | + + + Scenario: Testbot - Coordinates are from different small component and snap to big CC + Given a grid size of 300 meters + Given the extract extra arguments "--small-component-size 4" + Given the node map + """ + a b f h + d e g i + """ + + And the ways + | nodes | + | ab | + | be | + | ed | + | da | + | fg | + | hi | + + When I request a travel time matrix I should get + | | f | g | h | i | + | f | 0 | 30 | 0 | 30 | + | g | 30 | 0 | 30 | 0 | + | h | 0 | 30 | 0 | 30 | + | i | 30 | 0 | 30 | 0 | + + + Scenario: Testbot - Travel time matrix with loops + Given the node map + """ + a 1 2 b + d 4 3 c + """ + + And the ways + | nodes | oneway | + | ab | yes | + | bc | yes | + | cd | yes | + | da | yes | + + When I request a travel time matrix I should get + | | 1 | 2 | 3 | 4 | + | 1 | 0 | 10 +-1 | 40 +-1 | 50 +-1 | + | 2 | 70 +-1 | 0 | 30 +-1 | 40 +-1 | + | 3 | 40 +-1 | 50 +-1 | 0 | 10 +-1 | + | 4 | 30 +-1 | 40 +-1 | 70 +-1 | 0 | + + + Scenario: Testbot - Travel time matrix based on segment durations + Given the profile file + """ + local functions = require('testbot') + functions.setup_testbot = functions.setup + + functions.setup = function() + local profile = functions.setup_testbot() + profile.traffic_signal_penalty = 0 + profile.u_turn_penalty = 0 + return profile + end + + functions.process_segment = function(profile, segment) + segment.weight = 2 + segment.duration = 11 + end + + return functions + """ + + And the node map + """ + a-b-c-d + . + e + """ + + And the ways + | nodes | + | abcd | + | ce | + + When I request a travel time matrix I should get + | | a | b | c | d | e | + | a | 0 | 11 | 22 | 33 | 33 | + | b | 11 | 0 | 11 | 22 | 22 | + | c | 22 | 11 | 0 | 11 | 11 | + | d | 33 | 22 | 11 | 0 | 22 | + | e | 33 | 22 | 11 | 22 | 0 | + + + Scenario: Testbot - Travel time matrix for alternative loop paths + Given the profile file + """ + local functions = require('testbot') + functions.setup_testbot = functions.setup + + functions.setup = function() + local profile = functions.setup_testbot() + profile.traffic_signal_penalty = 0 + profile.u_turn_penalty = 0 + profile.weight_precision = 3 + return profile + end + + functions.process_segment = function(profile, segment) + segment.weight = 777 + segment.duration = 3 + end + + return functions + """ + And the node map + """ + a 2 1 b + 7 4 + 8 3 + c 5 6 d + """ + + And the ways + | nodes | oneway | + | ab | yes | + | bd | yes | + | dc | yes | + | ca | yes | + + When I request a travel time matrix I should get + | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | + | 1 | 0 | 11 | 3 | 2 | 6 | 5 | 8.9 | 7.9 | + | 2 | 1 | 0 | 4 | 3 | 7 | 6 | 9.9 | 8.9 | + | 3 | 9 | 8 | 0 | 11 | 3 | 2 | 5.9 | 4.9 | + | 4 | 10 | 9 | 1 | 0 | 4 | 3 | 6.9 | 5.9 | + | 5 | 6 | 5 | 9 | 8 | 0 | 11 | 2.9 | 1.9 | + | 6 | 7 | 6 | 10 | 9 | 1 | 0 | 3.9 | 2.9 | + | 7 | 3.1 | 2.1 | 6.1 | 5.1 | 9.1 | 8.1 | 0 | 11 | + | 8 | 4.1 | 3.1 | 7.1 | 6.1 | 10.1 | 9.1 | 1 | 0 | + + + Scenario: Testbot - Travel time matrix with ties + Given the profile file + """ + local functions = require('testbot') + functions.process_segment = function(profile, segment) + segment.weight = 1 + segment.duration = 1 + end + functions.process_turn = function(profile, turn) + if turn.angle >= 0 then + turn.duration = 16 + else + turn.duration = 4 + end + turn.weight = 0 + end + return functions + """ + And the node map + """ + a b + + c d + """ + + And the ways + | nodes | + | ab | + | ac | + | bd | + | dc | + + When I route I should get + | from | to | route | distance | time | weight | + | a | c | ac,ac | 200m | 5s | 5 | + + When I request a travel time matrix I should get + | | a | b | c | d | + | a | 0 | 1 | 5 | 10 | + + When I request a travel time matrix I should get + | | a | + | a | 0 | + | b | 1 | + | c | 15 | + | d | 10 | + + Scenario: Testbot - OneToMany vs ManyToOne + Given the node map + """ + a b + c + """ + + And the ways + | nodes | oneway | + | ab | yes | + | ac | | + | bc | | + + When I request a travel time matrix I should get + | | a | b | + | b | 24.1 | 0 | + + When I request a travel time matrix I should get + | | a | + | a | 0 | + | b | 24.1 | diff --git a/features/testbot/multi_level_routing.feature b/features/testbot/multi_level_routing.feature index 739faf08887..1fd0ab3999b 100644 --- a/features/testbot/multi_level_routing.feature +++ b/features/testbot/multi_level_routing.feature @@ -106,6 +106,40 @@ Feature: Multi level routing | l | 144.7 | 60 | | o | 124.7 | 0 | + + When I request a travel distance matrix I should get + | | a | f | l | o | + | a | 0+-2 | 2287+-2 | 1443+-2 | 1243+-2 | + | f | 2284+-2 | 0+-2 | 1241+-2 | 1443+-2 | + | l | 1443+-2 | 1244+-2 | 0+-2 | 600+-2 | + | o | 1243+-2 | 1444+-2 | 600+-2 | 0+-2 | + + When I request a travel distance matrix I should get + | | a | f | l | o | + | a | 0 | 2287.2+-2 | 1443+-2 | 1243+-2 | + + When I request a travel distance matrix I should get + | | a | + | a | 0 | + | f | 2284.5+-2 | + | l | 1443.1 | + | o | 1243 | + + When I request a travel distance matrix I should get + | | a | f | l | o | + | a | 0 | 2287+-2 | 1443+-2 | 1243+-2 | + | o | 1243 | 1444+-2 | 600+-2 | 0+-2 | + + + When I request a travel distance matrix I should get + | | a | o | + | a | 0+-2 | 1243+-2 | + | f | 2284+-2 | 1443+-2 | + | l | 1443+-2 | 600+-2 | + | o | 1243+-2 | 0+-2 | + + + Scenario: Testbot - Multi level routing: horizontal road Given the node map """ diff --git a/features/testbot/traffic_speeds.feature b/features/testbot/traffic_speeds.feature index 4e2b4ea2656..f5f2198f354 100644 --- a/features/testbot/traffic_speeds.feature +++ b/features/testbot/traffic_speeds.feature @@ -54,7 +54,7 @@ Feature: Traffic - speeds | a | d | ad,ad | 27 km/h | 1275.7,0 | 1 | | d | c | dc,dc | 36 km/h | 956.8,0 | 0 | | g | b | fb,fb | 36 km/h | 164.7,0 | 0 | - | a | g | ad,df,fb,fb | 30 km/h | 1275.7,487.5,304.7,0 | 1:0:0 | + | a | g | ad,df,fb,fb | 30 km/h | 1295.7,487.5,304.7,0 | 1:0:0 | Scenario: Weighting based on speed file weights, ETA based on file durations diff --git a/include/customizer/customizer_config.hpp b/include/customizer/customizer_config.hpp index f889dc2e666..35ab1a35628 100644 --- a/include/customizer/customizer_config.hpp +++ b/include/customizer/customizer_config.hpp @@ -21,7 +21,8 @@ struct CustomizationConfig final : storage::IOConfig ".osrm.partition", ".osrm.cells", ".osrm.ebg_nodes", - ".osrm.properties"}, + ".osrm.properties", + ".osrm.enw"}, {}, {".osrm.cell_metrics", ".osrm.mldgr"}), requested_num_threads(0) diff --git a/include/customizer/edge_based_graph.hpp b/include/customizer/edge_based_graph.hpp index 10a088dbd9d..8414c594283 100644 --- a/include/customizer/edge_based_graph.hpp +++ b/include/customizer/edge_based_graph.hpp @@ -16,28 +16,109 @@ namespace osrm namespace customizer { -using EdgeBasedGraphEdgeData = partitioner::EdgeBasedGraphEdgeData; - -struct MultiLevelEdgeBasedGraph - : public partitioner::MultiLevelGraph +struct EdgeBasedGraphEdgeData { - using Base = - partitioner::MultiLevelGraph; - using Base::Base; + NodeID turn_id; // ID of the edge based node (node based edge) }; -struct MultiLevelEdgeBasedGraphView - : public partitioner::MultiLevelGraph +template class MultiLevelGraph; + +namespace serialization { - using Base = partitioner::MultiLevelGraph; - using Base::Base; -}; +template +void read(storage::tar::FileReader &reader, + const std::string &name, + MultiLevelGraph &graph); + +template +void write(storage::tar::FileWriter &writer, + const std::string &name, + const MultiLevelGraph &graph); +} -struct StaticEdgeBasedGraphEdge : MultiLevelEdgeBasedGraph::InputEdge +template +class MultiLevelGraph : public partitioner::MultiLevelGraph { - using Base = MultiLevelEdgeBasedGraph::InputEdge; - using Base::Base; + private: + using SuperT = partitioner::MultiLevelGraph; + using PartitionerGraphT = partitioner::MultiLevelGraph; + template using Vector = util::ViewOrVector; + + public: + using NodeArrayEntry = typename SuperT::NodeArrayEntry; + using EdgeArrayEntry = typename SuperT::EdgeArrayEntry; + using EdgeOffset = typename SuperT::EdgeOffset; + + MultiLevelGraph() = default; + MultiLevelGraph(MultiLevelGraph &&) = default; + MultiLevelGraph(const MultiLevelGraph &) = default; + MultiLevelGraph &operator=(MultiLevelGraph &&) = default; + MultiLevelGraph &operator=(const MultiLevelGraph &) = default; + + MultiLevelGraph(PartitionerGraphT &&graph, + Vector node_weights_, + Vector node_durations_) + : node_weights(std::move(node_weights_)), node_durations(std::move(node_durations_)) + { + util::ViewOrVector + original_edge_array; + + std::tie(SuperT::node_array, + original_edge_array, + SuperT::node_to_edge_offset, + SuperT::connectivity_checksum) = std::move(graph).data(); + + SuperT::edge_array.reserve(original_edge_array.size()); + for (const auto &edge : original_edge_array) + { + SuperT::edge_array.push_back({edge.target, {edge.data.turn_id}}); + is_forward_edge.push_back(edge.data.forward); + is_backward_edge.push_back(edge.data.backward); + } + } + + MultiLevelGraph(Vector node_array_, + Vector edge_array_, + Vector node_to_edge_offset_, + Vector node_weights_, + Vector node_durations_, + Vector is_forward_edge_, + Vector is_backward_edge_) + : SuperT(std::move(node_array_), std::move(edge_array_), std::move(node_to_edge_offset_)), + node_weights(std::move(node_weights_)), node_durations(std::move(node_durations_)), + is_forward_edge(is_forward_edge_), is_backward_edge(is_backward_edge_) + { + } + + EdgeWeight GetNodeWeight(NodeID node) const { return node_weights[node]; } + + EdgeWeight GetNodeDuration(NodeID node) const { return node_durations[node]; } + + bool IsForwardEdge(EdgeID edge) const { return is_forward_edge[edge]; } + + bool IsBackwardEdge(EdgeID edge) const { return is_backward_edge[edge]; } + + friend void + serialization::read(storage::tar::FileReader &reader, + const std::string &name, + MultiLevelGraph &graph); + friend void + serialization::write(storage::tar::FileWriter &writer, + const std::string &name, + const MultiLevelGraph &graph); + + protected: + Vector node_weights; + Vector node_durations; + Vector is_forward_edge; + Vector is_backward_edge; }; + +using MultiLevelEdgeBasedGraph = + MultiLevelGraph; +using MultiLevelEdgeBasedGraphView = + MultiLevelGraph; } } diff --git a/include/customizer/files.hpp b/include/customizer/files.hpp index c89b5cb7607..364a2572d23 100644 --- a/include/customizer/files.hpp +++ b/include/customizer/files.hpp @@ -73,6 +73,39 @@ writeCellMetrics(const boost::filesystem::path &path, } } } + +// reads .osrm.mldgr file +template +inline void readGraph(const boost::filesystem::path &path, + MultiLevelGraphT &graph, + std::uint32_t &connectivity_checksum) +{ + static_assert(std::is_same::value || + std::is_same::value, + ""); + + storage::tar::FileReader reader{path, storage::tar::FileReader::VerifyFingerprint}; + + reader.ReadInto("/mld/connectivity_checksum", connectivity_checksum); + serialization::read(reader, "/mld/multilevelgraph", graph); +} + +// writes .osrm.mldgr file +template +inline void writeGraph(const boost::filesystem::path &path, + const MultiLevelGraphT &graph, + const std::uint32_t connectivity_checksum) +{ + static_assert(std::is_same::value || + std::is_same::value, + ""); + + storage::tar::FileWriter writer{path, storage::tar::FileWriter::GenerateFingerprint}; + + writer.WriteElementCount64("/mld/connectivity_checksum", 1); + writer.WriteFrom("/mld/connectivity_checksum", connectivity_checksum); + serialization::write(writer, "/mld/multilevelgraph", graph); +} } } } diff --git a/include/customizer/serialization.hpp b/include/customizer/serialization.hpp index 973e2517db4..c5c5fdee28e 100644 --- a/include/customizer/serialization.hpp +++ b/include/customizer/serialization.hpp @@ -1,6 +1,8 @@ #ifndef OSRM_CUSTOMIZER_SERIALIZATION_HPP #define OSRM_CUSTOMIZER_SERIALIZATION_HPP +#include "customizer/edge_based_graph.hpp" + #include "partitioner/cell_storage.hpp" #include "storage/serialization.hpp" @@ -31,6 +33,34 @@ inline void write(storage::tar::FileWriter &writer, storage::serialization::write(writer, name + "/weights", metric.weights); storage::serialization::write(writer, name + "/durations", metric.durations); } + +template +inline void read(storage::tar::FileReader &reader, + const std::string &name, + MultiLevelGraph &graph) +{ + storage::serialization::read(reader, name + "/node_array", graph.node_array); + storage::serialization::read(reader, name + "/node_weights", graph.node_weights); + storage::serialization::read(reader, name + "/node_durations", graph.node_durations); + storage::serialization::read(reader, name + "/edge_array", graph.edge_array); + storage::serialization::read(reader, name + "/is_forward_edge", graph.is_forward_edge); + storage::serialization::read(reader, name + "/is_backward_edge", graph.is_backward_edge); + storage::serialization::read(reader, name + "/node_to_edge_offset", graph.node_to_edge_offset); +} + +template +inline void write(storage::tar::FileWriter &writer, + const std::string &name, + const MultiLevelGraph &graph) +{ + storage::serialization::write(writer, name + "/node_array", graph.node_array); + storage::serialization::write(writer, name + "/node_weights", graph.node_weights); + storage::serialization::write(writer, name + "/node_durations", graph.node_durations); + storage::serialization::write(writer, name + "/edge_array", graph.edge_array); + storage::serialization::write(writer, name + "/is_forward_edge", graph.is_forward_edge); + storage::serialization::write(writer, name + "/is_backward_edge", graph.is_backward_edge); + storage::serialization::write(writer, name + "/node_to_edge_offset", graph.node_to_edge_offset); +} } } } diff --git a/include/engine/algorithm.hpp b/include/engine/algorithm.hpp index 8f48b373005..760f78e80a5 100644 --- a/include/engine/algorithm.hpp +++ b/include/engine/algorithm.hpp @@ -50,6 +50,9 @@ template struct HasMapMatching final : std::false_type template struct HasManyToManySearch final : std::false_type { }; +template struct SupportsDistanceAnnotationType final : std::false_type +{ +}; template struct HasGetTileTurns final : std::false_type { }; @@ -73,6 +76,9 @@ template <> struct HasMapMatching final : std::true_type template <> struct HasManyToManySearch final : std::true_type { }; +template <> struct SupportsDistanceAnnotationType final : std::true_type +{ +}; template <> struct HasGetTileTurns final : std::true_type { }; @@ -96,6 +102,9 @@ template <> struct HasMapMatching final : std::true_type template <> struct HasManyToManySearch final : std::true_type { }; +template <> struct SupportsDistanceAnnotationType final : std::false_type +{ +}; template <> struct HasGetTileTurns final : std::true_type { }; diff --git a/include/engine/api/table_api.hpp b/include/engine/api/table_api.hpp index 2ff667404ab..7f8cb770615 100644 --- a/include/engine/api/table_api.hpp +++ b/include/engine/api/table_api.hpp @@ -36,9 +36,10 @@ class TableAPI final : public BaseAPI { } - virtual void MakeResponse(const std::vector &durations, - const std::vector &phantoms, - util::json::Object &response) const + virtual void + MakeResponse(const std::pair, std::vector> &tables, + const std::vector &phantoms, + util::json::Object &response) const { auto number_of_sources = parameters.sources.size(); auto number_of_destinations = parameters.destinations.size(); @@ -64,8 +65,18 @@ class TableAPI final : public BaseAPI response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations); } - response.values["durations"] = - MakeTable(durations, number_of_sources, number_of_destinations); + if (parameters.annotations & TableParameters::AnnotationsType::Duration) + { + response.values["durations"] = + MakeDurationTable(tables.first, number_of_sources, number_of_destinations); + } + + if (parameters.annotations & TableParameters::AnnotationsType::Distance) + { + response.values["distances"] = + MakeDistanceTable(tables.second, number_of_sources, number_of_destinations); + } + response.values["code"] = "Ok"; } @@ -97,9 +108,9 @@ class TableAPI final : public BaseAPI return json_waypoints; } - virtual util::json::Array MakeTable(const std::vector &values, - std::size_t number_of_rows, - std::size_t number_of_columns) const + virtual util::json::Array MakeDurationTable(const std::vector &values, + std::size_t number_of_rows, + std::size_t number_of_columns) const { util::json::Array json_table; for (const auto row : util::irange(0UL, number_of_rows)) @@ -116,6 +127,7 @@ class TableAPI final : public BaseAPI { return util::json::Value(util::json::Null()); } + // division by 10 because the duration is in deciseconds (10s) return util::json::Value(util::json::Number(duration / 10.)); }); json_table.values.push_back(std::move(json_row)); @@ -123,6 +135,34 @@ class TableAPI final : public BaseAPI return json_table; } + virtual util::json::Array MakeDistanceTable(const std::vector &values, + std::size_t number_of_rows, + std::size_t number_of_columns) const + { + util::json::Array json_table; + for (const auto row : util::irange(0UL, number_of_rows)) + { + util::json::Array json_row; + auto row_begin_iterator = values.begin() + (row * number_of_columns); + auto row_end_iterator = values.begin() + ((row + 1) * number_of_columns); + json_row.values.resize(number_of_columns); + std::transform(row_begin_iterator, + row_end_iterator, + json_row.values.begin(), + [](const EdgeDistance distance) { + if (distance == INVALID_EDGE_DISTANCE) + { + return util::json::Value(util::json::Null()); + } + // round to single decimal place + return util::json::Value( + util::json::Number(std::round(distance * 10) / 10.)); + }); + json_table.values.push_back(std::move(json_row)); + } + return json_table; + } + const TableParameters ¶meters; }; diff --git a/include/engine/api/table_parameters.hpp b/include/engine/api/table_parameters.hpp index 58d936c7347..4f1a496bd6f 100644 --- a/include/engine/api/table_parameters.hpp +++ b/include/engine/api/table_parameters.hpp @@ -60,6 +60,16 @@ struct TableParameters : public BaseParameters std::vector sources; std::vector destinations; + enum class AnnotationsType + { + None = 0, + Duration = 0x01, + Distance = 0x02, + All = Duration | Distance + }; + + AnnotationsType annotations = AnnotationsType::Duration; + TableParameters() = default; template TableParameters(std::vector sources_, @@ -70,6 +80,16 @@ struct TableParameters : public BaseParameters { } + template + TableParameters(std::vector sources_, + std::vector destinations_, + const AnnotationsType annotations_, + Args... args_) + : BaseParameters{std::forward(args_)...}, sources{std::move(sources_)}, + destinations{std::move(destinations_)}, annotations{annotations_} + { + } + bool IsValid() const { if (!BaseParameters::IsValid()) @@ -79,7 +99,7 @@ struct TableParameters : public BaseParameters if (coordinates.size() < 2) return false; - // 1/ The user is able to specify duplicates in srcs and dsts, in that case it's her fault + // 1/ The user is able to specify duplicates in srcs and dsts, in that case it's their fault // 2/ len(srcs) and len(dsts) smaller or equal to len(locations) if (sources.size() > coordinates.size()) @@ -100,6 +120,26 @@ struct TableParameters : public BaseParameters return true; } }; +inline bool operator&(TableParameters::AnnotationsType lhs, TableParameters::AnnotationsType rhs) +{ + return static_cast( + static_cast>(lhs) & + static_cast>(rhs)); +} + +inline TableParameters::AnnotationsType operator|(TableParameters::AnnotationsType lhs, + TableParameters::AnnotationsType rhs) +{ + return (TableParameters::AnnotationsType)( + static_cast>(lhs) | + static_cast>(rhs)); +} + +inline TableParameters::AnnotationsType &operator|=(TableParameters::AnnotationsType &lhs, + TableParameters::AnnotationsType rhs) +{ + return lhs = lhs | rhs; +} } } } diff --git a/include/engine/datafacade/algorithm_datafacade.hpp b/include/engine/datafacade/algorithm_datafacade.hpp index cdf49c286f5..ef910d4ac22 100644 --- a/include/engine/datafacade/algorithm_datafacade.hpp +++ b/include/engine/datafacade/algorithm_datafacade.hpp @@ -2,6 +2,7 @@ #define OSRM_ENGINE_DATAFACADE_ALGORITHM_DATAFACADE_HPP #include "contractor/query_edge.hpp" +#include "customizer/edge_based_graph.hpp" #include "extractor/edge_based_edge.hpp" #include "engine/algorithm.hpp" @@ -59,7 +60,7 @@ template <> class AlgorithmDataFacade template <> class AlgorithmDataFacade { public: - using EdgeData = extractor::EdgeBasedEdge::EdgeData; + using EdgeData = customizer::EdgeBasedGraphEdgeData; using EdgeRange = util::range; // search graph access @@ -71,12 +72,20 @@ template <> class AlgorithmDataFacade virtual unsigned GetOutDegree(const NodeID n) const = 0; + virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0; + + virtual EdgeWeight GetNodeWeight(const NodeID node) const = 0; + + virtual EdgeWeight GetNodeDuration(const NodeID node) const = 0; // TODO: to be removed + + virtual bool IsForwardEdge(EdgeID edge) const = 0; + + virtual bool IsBackwardEdge(EdgeID edge) const = 0; + virtual NodeID GetTarget(const EdgeID e) const = 0; virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0; - virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0; - virtual const partitioner::MultiLevelPartitionView &GetMultiLevelPartition() const = 0; virtual const partitioner::CellStorageView &GetCellStorage() const = 0; diff --git a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp index 846a1a04c77..6befce6d08d 100644 --- a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp +++ b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp @@ -133,7 +133,6 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade using RTreeNode = SharedRTree::TreeNode; extractor::ClassData exclude_mask; - std::string m_timestamp; extractor::ProfileProperties *m_profile_properties; extractor::Datasources *m_datasources; @@ -283,13 +282,13 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade return segment_data.GetReverseDatasources(id); } - TurnPenalty GetWeightPenaltyForEdgeID(const unsigned id) const override final + TurnPenalty GetWeightPenaltyForEdgeID(const EdgeID id) const override final { BOOST_ASSERT(m_turn_weight_penalties.size() > id); return m_turn_weight_penalties[id]; } - TurnPenalty GetDurationPenaltyForEdgeID(const unsigned id) const override final + TurnPenalty GetDurationPenaltyForEdgeID(const EdgeID id) const override final { BOOST_ASSERT(m_turn_duration_penalties.size() > id); return m_turn_duration_penalties[id]; @@ -622,7 +621,6 @@ class ContiguousInternalMemoryDataFacade const std::size_t exclude_index) : ContiguousInternalMemoryDataFacadeBase(allocator, metric_name, exclude_index), ContiguousInternalMemoryAlgorithmDataFacade(allocator, metric_name, exclude_index) - { } }; @@ -684,16 +682,36 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade : public Algo return query_graph.GetOutDegree(n); } - NodeID GetTarget(const EdgeID e) const override final { return query_graph.GetTarget(e); } + EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final + { + return query_graph.GetAdjacentEdgeRange(node); + } - const EdgeData &GetEdgeData(const EdgeID e) const override final + EdgeWeight GetNodeWeight(const NodeID node) const override final { - return query_graph.GetEdgeData(e); + return query_graph.GetNodeWeight(node); } - EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final + EdgeDuration GetNodeDuration(const NodeID node) const override final { - return query_graph.GetAdjacentEdgeRange(node); + return query_graph.GetNodeDuration(node); + } + + bool IsForwardEdge(const NodeID node) const override final + { + return query_graph.IsForwardEdge(node); + } + + bool IsBackwardEdge(const NodeID node) const override final + { + return query_graph.IsBackwardEdge(node); + } + + NodeID GetTarget(const EdgeID e) const override final { return query_graph.GetTarget(e); } + + const EdgeData &GetEdgeData(const EdgeID e) const override final + { + return query_graph.GetEdgeData(e); } EdgeRange GetBorderEdgeRange(const LevelID level, const NodeID node) const override final @@ -720,7 +738,6 @@ class ContiguousInternalMemoryDataFacade final const std::size_t exclude_index) : ContiguousInternalMemoryDataFacadeBase(allocator, metric_name, exclude_index), ContiguousInternalMemoryAlgorithmDataFacade(allocator, metric_name, exclude_index) - { } }; diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index beb71674ee0..091017859b3 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -33,7 +33,6 @@ #include #include - #include #include @@ -87,9 +86,9 @@ class BaseDataFacade virtual NodeForwardRange GetUncompressedForwardGeometry(const EdgeID id) const = 0; virtual NodeReverseRange GetUncompressedReverseGeometry(const EdgeID id) const = 0; - virtual TurnPenalty GetWeightPenaltyForEdgeID(const unsigned id) const = 0; + virtual TurnPenalty GetWeightPenaltyForEdgeID(const EdgeID id) const = 0; - virtual TurnPenalty GetDurationPenaltyForEdgeID(const unsigned id) const = 0; + virtual TurnPenalty GetDurationPenaltyForEdgeID(const EdgeID id) const = 0; // Gets the weight values for each segment in an uncompressed geometry. // Should always be 1 shorter than GetUncompressedGeometry diff --git a/include/engine/geospatial_query.hpp b/include/engine/geospatial_query.hpp index b9a709d0cb7..4ecfc8118bd 100644 --- a/include/engine/geospatial_query.hpp +++ b/include/engine/geospatial_query.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -447,6 +448,8 @@ template class GeospatialQuery const auto forward_durations = datafacade.GetUncompressedForwardDurations(geometry_id); const auto reverse_durations = datafacade.GetUncompressedReverseDurations(geometry_id); + const auto forward_geometry = datafacade.GetUncompressedForwardGeometry(geometry_id); + const auto forward_weight_offset = std::accumulate(forward_weights.begin(), forward_weights.begin() + data.fwd_segment_position, @@ -457,12 +460,25 @@ template class GeospatialQuery forward_durations.begin() + data.fwd_segment_position, EdgeDuration{0}); - EdgeWeight forward_weight = forward_weights[data.fwd_segment_position]; - EdgeDuration forward_duration = forward_durations[data.fwd_segment_position]; + EdgeDistance forward_distance_offset = 0; + for (auto current = forward_geometry.begin(); + current < forward_geometry.begin() + data.fwd_segment_position; + ++current) + { + forward_distance_offset += util::coordinate_calculation::fccApproximateDistance( + datafacade.GetCoordinateOfNode(*current), + datafacade.GetCoordinateOfNode(*std::next(current))); + } BOOST_ASSERT(data.fwd_segment_position < std::distance(forward_durations.begin(), forward_durations.end())); + EdgeWeight forward_weight = forward_weights[data.fwd_segment_position]; + EdgeDuration forward_duration = forward_durations[data.fwd_segment_position]; + EdgeDistance forward_distance = util::coordinate_calculation::fccApproximateDistance( + datafacade.GetCoordinateOfNode(forward_geometry(data.fwd_segment_position)), + point_on_segment); + const auto reverse_weight_offset = std::accumulate(reverse_weights.begin(), reverse_weights.end() - data.fwd_segment_position - 1, @@ -473,10 +489,23 @@ template class GeospatialQuery reverse_durations.end() - data.fwd_segment_position - 1, EdgeDuration{0}); + EdgeDistance reverse_distance_offset = 0; + for (auto current = forward_geometry.begin(); + current < forward_geometry.end() - data.fwd_segment_position - 2; + ++current) + { + reverse_distance_offset += util::coordinate_calculation::fccApproximateDistance( + datafacade.GetCoordinateOfNode(*current), + datafacade.GetCoordinateOfNode(*std::next(current))); + } + EdgeWeight reverse_weight = reverse_weights[reverse_weights.size() - data.fwd_segment_position - 1]; EdgeDuration reverse_duration = reverse_durations[reverse_durations.size() - data.fwd_segment_position - 1]; + EdgeDistance reverse_distance = util::coordinate_calculation::fccApproximateDistance( + point_on_segment, + datafacade.GetCoordinateOfNode(forward_geometry(data.fwd_segment_position + 1))); ratio = std::min(1.0, std::max(0.0, ratio)); if (data.forward_segment_id.id != SPECIAL_SEGMENTID) @@ -510,6 +539,10 @@ template class GeospatialQuery reverse_weight, forward_weight_offset, reverse_weight_offset, + forward_distance, + reverse_distance, + forward_distance_offset, + reverse_distance_offset, forward_duration, reverse_duration, forward_duration_offset, diff --git a/include/engine/hint.hpp b/include/engine/hint.hpp index 7334b673796..3ab69b928e0 100644 --- a/include/engine/hint.hpp +++ b/include/engine/hint.hpp @@ -63,8 +63,8 @@ struct Hint friend std::ostream &operator<<(std::ostream &, const Hint &); }; -static_assert(sizeof(Hint) == 64 + 4, "Hint is bigger than expected"); -constexpr std::size_t ENCODED_HINT_SIZE = 92; +static_assert(sizeof(Hint) == 80 + 4, "Hint is bigger than expected"); +constexpr std::size_t ENCODED_HINT_SIZE = 112; static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint), "ENCODED_HINT_SIZE does not match size of Hint"); } diff --git a/include/engine/phantom_node.hpp b/include/engine/phantom_node.hpp index 9357c009e10..3cb0b959369 100644 --- a/include/engine/phantom_node.hpp +++ b/include/engine/phantom_node.hpp @@ -47,10 +47,13 @@ struct PhantomNode : forward_segment_id{SPECIAL_SEGMENTID, false}, reverse_segment_id{SPECIAL_SEGMENTID, false}, forward_weight(INVALID_EDGE_WEIGHT), reverse_weight(INVALID_EDGE_WEIGHT), forward_weight_offset(0), reverse_weight_offset(0), + forward_distance(INVALID_EDGE_DISTANCE), reverse_distance(INVALID_EDGE_DISTANCE), + forward_distance_offset(0), reverse_distance_offset(0), forward_duration(MAXIMAL_EDGE_DURATION), reverse_duration(MAXIMAL_EDGE_DURATION), forward_duration_offset(0), reverse_duration_offset(0), fwd_segment_position(0), is_valid_forward_source{false}, is_valid_forward_target{false}, is_valid_reverse_source{false}, is_valid_reverse_target{false}, bearing(0) + { } @@ -78,6 +81,30 @@ struct PhantomNode return reverse_duration + reverse_duration_offset; } + // DO THIS FOR DISTANCE + + EdgeDistance GetForwardDistance() const + { + // ..... <-- forward_distance + // .... <-- offset + // ......... <-- desired distance + // x <-- this is PhantomNode.location + // 0----1----2----3----4 <-- EdgeBasedGraph Node segments + BOOST_ASSERT(forward_segment_id.enabled); + return forward_distance + forward_distance_offset; + } + + EdgeDistance GetReverseDistance() const + { + // .......... <-- reverse_distance + // ... <-- offset + // ............. <-- desired distance + // x <-- this is PhantomNode.location + // 0----1----2----3----4 <-- EdgeBasedGraph Node segments + BOOST_ASSERT(reverse_segment_id.enabled); + return reverse_distance + reverse_distance_offset; + } + bool IsBidirected() const { return forward_segment_id.enabled && reverse_segment_id.enabled; } bool IsValid(const unsigned number_of_nodes) const @@ -88,6 +115,8 @@ struct PhantomNode (reverse_weight != INVALID_EDGE_WEIGHT)) && ((forward_duration != MAXIMAL_EDGE_DURATION) || (reverse_duration != MAXIMAL_EDGE_DURATION)) && + ((forward_distance != INVALID_EDGE_DISTANCE) || + (reverse_distance != INVALID_EDGE_DISTANCE)) && (component.id != INVALID_COMPONENTID); } @@ -130,6 +159,10 @@ struct PhantomNode EdgeWeight reverse_weight, EdgeWeight forward_weight_offset, EdgeWeight reverse_weight_offset, + EdgeDistance forward_distance, + EdgeDistance reverse_distance, + EdgeDistance forward_distance_offset, + EdgeDistance reverse_distance_offset, EdgeWeight forward_duration, EdgeWeight reverse_duration, EdgeWeight forward_duration_offset, @@ -144,7 +177,9 @@ struct PhantomNode : forward_segment_id{other.forward_segment_id}, reverse_segment_id{other.reverse_segment_id}, forward_weight{forward_weight}, reverse_weight{reverse_weight}, forward_weight_offset{forward_weight_offset}, - reverse_weight_offset{reverse_weight_offset}, forward_duration{forward_duration}, + reverse_weight_offset{reverse_weight_offset}, forward_distance{forward_distance}, + reverse_distance{reverse_distance}, forward_distance_offset{forward_distance_offset}, + reverse_distance_offset{reverse_distance_offset}, forward_duration{forward_duration}, reverse_duration{reverse_duration}, forward_duration_offset{forward_duration_offset}, reverse_duration_offset{reverse_duration_offset}, component{component.id, component.is_tiny}, location{location}, @@ -162,13 +197,17 @@ struct PhantomNode EdgeWeight reverse_weight; EdgeWeight forward_weight_offset; // TODO: try to remove -> requires path unpacking changes EdgeWeight reverse_weight_offset; // TODO: try to remove -> requires path unpacking changes + EdgeDistance forward_distance; + EdgeDistance reverse_distance; + EdgeDistance forward_distance_offset; // TODO: try to remove -> requires path unpacking changes + EdgeDistance reverse_distance_offset; // TODO: try to remove -> requires path unpacking changes EdgeWeight forward_duration; EdgeWeight reverse_duration; EdgeWeight forward_duration_offset; // TODO: try to remove -> requires path unpacking changes EdgeWeight reverse_duration_offset; // TODO: try to remove -> requires path unpacking changes ComponentID component; - util::Coordinate location; + util::Coordinate location; // this is the coordinate of x util::Coordinate input_location; unsigned short fwd_segment_position; // is phantom node valid to be used as source or target @@ -180,7 +219,7 @@ struct PhantomNode unsigned short bearing : 12; }; -static_assert(sizeof(PhantomNode) == 64, "PhantomNode has more padding then expected"); +static_assert(sizeof(PhantomNode) == 80, "PhantomNode has more padding then expected"); using PhantomNodePair = std::pair; diff --git a/include/engine/routing_algorithms.hpp b/include/engine/routing_algorithms.hpp index dd8bcc8dde5..aec19f753d3 100644 --- a/include/engine/routing_algorithms.hpp +++ b/include/engine/routing_algorithms.hpp @@ -30,10 +30,12 @@ class RoutingAlgorithmsInterface virtual InternalRouteResult DirectShortestPathSearch(const PhantomNodes &phantom_node_pair) const = 0; - virtual std::vector + virtual std::pair, std::vector> ManyToManySearch(const std::vector &phantom_nodes, const std::vector &source_indices, - const std::vector &target_indices) const = 0; + const std::vector &target_indices, + const bool calculate_distance, + const bool calculate_duration) const = 0; virtual routing_algorithms::SubMatchingList MapMatching(const routing_algorithms::CandidateLists &candidates_list, @@ -53,6 +55,7 @@ class RoutingAlgorithmsInterface virtual bool HasDirectShortestPathSearch() const = 0; virtual bool HasMapMatching() const = 0; virtual bool HasManyToManySearch() const = 0; + virtual bool SupportsDistanceAnnotationType() const = 0; virtual bool HasGetTileTurns() const = 0; virtual bool HasExcludeFlags() const = 0; virtual bool IsValid() const = 0; @@ -81,10 +84,12 @@ template class RoutingAlgorithms final : public RoutingAlgo InternalRouteResult DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const final override; - std::vector + virtual std::pair, std::vector> ManyToManySearch(const std::vector &phantom_nodes, const std::vector &source_indices, - const std::vector &target_indices) const final override; + const std::vector &target_indices, + const bool calculate_distance, + const bool calculate_duration) const final override; routing_algorithms::SubMatchingList MapMatching(const routing_algorithms::CandidateLists &candidates_list, @@ -124,6 +129,11 @@ template class RoutingAlgorithms final : public RoutingAlgo return routing_algorithms::HasManyToManySearch::value; } + bool SupportsDistanceAnnotationType() const final override + { + return routing_algorithms::SupportsDistanceAnnotationType::value; + } + bool HasGetTileTurns() const final override { return routing_algorithms::HasGetTileTurns::value; @@ -184,10 +194,12 @@ inline routing_algorithms::SubMatchingList RoutingAlgorithms::MapMatc } template -std::vector RoutingAlgorithms::ManyToManySearch( - const std::vector &phantom_nodes, - const std::vector &_source_indices, - const std::vector &_target_indices) const +std::pair, std::vector> +RoutingAlgorithms::ManyToManySearch(const std::vector &phantom_nodes, + const std::vector &_source_indices, + const std::vector &_target_indices, + const bool calculate_distance, + const bool calculate_duration) const { BOOST_ASSERT(!phantom_nodes.empty()); @@ -205,8 +217,13 @@ std::vector RoutingAlgorithms::ManyToManySearch( std::iota(target_indices.begin(), target_indices.end(), 0); } - return routing_algorithms::manyToManySearch( - heaps, *facade, phantom_nodes, std::move(source_indices), std::move(target_indices)); + return routing_algorithms::manyToManySearch(heaps, + *facade, + phantom_nodes, + std::move(source_indices), + std::move(target_indices), + calculate_distance, + calculate_duration); } template diff --git a/include/engine/routing_algorithms/many_to_many.hpp b/include/engine/routing_algorithms/many_to_many.hpp index f4d21884645..281844abd18 100644 --- a/include/engine/routing_algorithms/many_to_many.hpp +++ b/include/engine/routing_algorithms/many_to_many.hpp @@ -15,23 +15,43 @@ namespace engine { namespace routing_algorithms { - namespace { struct NodeBucket { NodeID middle_node; - unsigned column_index; // a column in the weight/duration matrix + NodeID parent_node; + unsigned column_index : 31; // a column in the weight/duration matrix + unsigned from_clique_arc : 1; EdgeWeight weight; EdgeDuration duration; - NodeBucket(NodeID middle_node, unsigned column_index, EdgeWeight weight, EdgeDuration duration) - : middle_node(middle_node), column_index(column_index), weight(weight), duration(duration) + NodeBucket(NodeID middle_node, + NodeID parent_node, + bool from_clique_arc, + unsigned column_index, + EdgeWeight weight, + EdgeDuration duration) + : middle_node(middle_node), parent_node(parent_node), column_index(column_index), + from_clique_arc(from_clique_arc), weight(weight), duration(duration) + { + } + + NodeBucket(NodeID middle_node, + NodeID parent_node, + unsigned column_index, + EdgeWeight weight, + EdgeDuration duration) + : middle_node(middle_node), parent_node(parent_node), column_index(column_index), + from_clique_arc(false), weight(weight), duration(duration) { } // partial order comparison - bool operator<(const NodeBucket &rhs) const { return middle_node < rhs.middle_node; } + bool operator<(const NodeBucket &rhs) const + { + return std::tie(middle_node, column_index) < std::tie(rhs.middle_node, rhs.column_index); + } // functor for equal_range struct Compare @@ -46,15 +66,36 @@ struct NodeBucket return lhs < rhs.middle_node; } }; + + // functor for equal_range + struct ColumnCompare + { + unsigned column_idx; + + ColumnCompare(unsigned column_idx) : column_idx(column_idx){}; + + bool operator()(const NodeBucket &lhs, const NodeID &rhs) const // lowerbound + { + return std::tie(lhs.middle_node, lhs.column_index) < std::tie(rhs, column_idx); + } + + bool operator()(const NodeID &lhs, const NodeBucket &rhs) const // upperbound + { + return std::tie(lhs, column_idx) < std::tie(rhs.middle_node, rhs.column_index); + } + }; }; -} +} // namespace template -std::vector manyToManySearch(SearchEngineData &engine_working_data, - const DataFacade &facade, - const std::vector &phantom_nodes, - const std::vector &source_indices, - const std::vector &target_indices); +std::pair, std::vector> +manyToManySearch(SearchEngineData &engine_working_data, + const DataFacade &facade, + const std::vector &phantom_nodes, + const std::vector &source_indices, + const std::vector &target_indices, + const bool calculate_distance, + const bool calculate_duration); } // namespace routing_algorithms } // namespace engine diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index 90e5e73e2a8..0c0808a3f49 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -181,6 +181,7 @@ void annotatePath(const FacadeT &facade, BOOST_ASSERT(datasource_vector.size() > 0); BOOST_ASSERT(weight_vector.size() + 1 == id_vector.size()); BOOST_ASSERT(duration_vector.size() + 1 == id_vector.size()); + const bool is_first_segment = unpacked_path.empty(); const std::size_t start_index = @@ -405,6 +406,22 @@ InternalRouteResult extractRoute(const DataFacade &facade, return raw_route_data; } +template EdgeDistance computeEdgeDistance(const FacadeT &facade, NodeID node_id) +{ + const auto geometry_index = facade.GetGeometryIndex(node_id); + + EdgeDistance total_distance = 0.0; + + auto geometry_range = facade.GetUncompressedForwardGeometry(geometry_index.id); + for (auto current = geometry_range.begin(); current < geometry_range.end() - 1; ++current) + { + total_distance += util::coordinate_calculation::fccApproximateDistance( + facade.GetCoordinateOfNode(*current), facade.GetCoordinateOfNode(*std::next(current))); + } + + return total_distance; +} + } // namespace routing_algorithms } // namespace engine } // namespace osrm diff --git a/include/engine/routing_algorithms/routing_base_ch.hpp b/include/engine/routing_algorithms/routing_base_ch.hpp index 5ecbc6b2928..051b5f8c4a6 100644 --- a/include/engine/routing_algorithms/routing_base_ch.hpp +++ b/include/engine/routing_algorithms/routing_base_ch.hpp @@ -288,6 +288,106 @@ void unpackPath(const DataFacade &facade, } } +template +EdgeDistance calculateEBGNodeAnnotations(const DataFacade &facade, + BidirectionalIterator packed_path_begin, + BidirectionalIterator packed_path_end) +{ + // Make sure we have at least something to unpack + if (packed_path_begin == packed_path_end || + std::distance(packed_path_begin, packed_path_end) <= 1) + return 0; + + std::stack> recursion_stack; + std::stack distance_stack; + // We have to push the path in reverse order onto the stack because it's LIFO. + for (auto current = std::prev(packed_path_end); current > packed_path_begin; + current = std::prev(current)) + { + recursion_stack.emplace(*std::prev(current), *current, false); + } + + std::tuple edge; + while (!recursion_stack.empty()) + { + edge = recursion_stack.top(); + recursion_stack.pop(); + + // Have we processed the edge before? tells us if we have values in the durations stack that + // we can add up + if (!std::get<2>(edge)) + { // haven't processed edge before, so process it in the body! + + std::get<2>(edge) = true; // mark that this edge will now be processed + + // Look for an edge on the forward CH graph (.forward) + EdgeID smaller_edge_id = + facade.FindSmallestEdge(std::get<0>(edge), std::get<1>(edge), [](const auto &data) { + return data.forward; + }); + + // If we didn't find one there, the we might be looking at a part of the path that + // was found using the backward search. Here, we flip the node order (.second, + // .first) and only consider edges with the `.backward` flag. + if (SPECIAL_EDGEID == smaller_edge_id) + { + smaller_edge_id = + facade.FindSmallestEdge(std::get<1>(edge), + std::get<0>(edge), + [](const auto &data) { return data.backward; }); + } + + // If we didn't find anything *still*, then something is broken and someone has + // called this function with bad values. + BOOST_ASSERT_MSG(smaller_edge_id != SPECIAL_EDGEID, "Invalid smaller edge ID"); + + const auto &data = facade.GetEdgeData(smaller_edge_id); + BOOST_ASSERT_MSG(data.weight != std::numeric_limits::max(), + "edge weight invalid"); + + // If the edge is a shortcut, we need to add the two halfs to the stack. + if (data.shortcut) + { // unpack + const NodeID middle_node_id = data.turn_id; + // Note the order here - we're adding these to a stack, so we + // want the first->middle to get visited before middle->second + recursion_stack.emplace(edge); + recursion_stack.emplace(middle_node_id, std::get<1>(edge), false); + recursion_stack.emplace(std::get<0>(edge), middle_node_id, false); + } + else + { + // compute the duration here and put it onto the duration stack using method + // similar to annotatePath but smaller + EdgeDistance distance = computeEdgeDistance(facade, std::get<0>(edge)); + distance_stack.emplace(distance); + } + } + else + { // the edge has already been processed. this means that there are enough values in the + // distances stack + + BOOST_ASSERT_MSG(distance_stack.size() >= 2, + "There are not enough (at least 2) values on the distance stack"); + EdgeDistance distance1 = distance_stack.top(); + distance_stack.pop(); + EdgeDistance distance2 = distance_stack.top(); + distance_stack.pop(); + EdgeDistance distance = distance1 + distance2; + distance_stack.emplace(distance); + } + } + + EdgeDistance total_distance = 0; + while (!distance_stack.empty()) + { + total_distance += distance_stack.top(); + distance_stack.pop(); + } + + return total_distance; +} + template void unpackPath(const FacadeT &facade, RandomIter packed_path_begin, @@ -340,6 +440,11 @@ void retrievePackedPathFromSingleHeap(const SearchEngineData::QueryHe const NodeID middle_node_id, std::vector &packed_path); +void retrievePackedPathFromSingleManyToManyHeap( + const SearchEngineData::ManyToManyQueryHeap &search_heap, + const NodeID middle_node_id, + std::vector &packed_path); + // assumes that heaps are already setup correctly. // ATTENTION: This only works if no additional offset is supplied next to the Phantom Node // Offsets. diff --git a/include/engine/routing_algorithms/routing_base_mld.hpp b/include/engine/routing_algorithms/routing_base_mld.hpp index cd4f0846a6a..ce9b23d70a0 100644 --- a/include/engine/routing_algorithms/routing_base_mld.hpp +++ b/include/engine/routing_algorithms/routing_base_mld.hpp @@ -54,7 +54,7 @@ inline bool checkParentCellRestriction(CellID, const PhantomNodes &) { return tr // Restricted search (Args is LevelID, CellID): // * use the fixed level for queries -// * check if the node cell is the same as the specified parent onr +// * check if the node cell is the same as the specified parent template inline LevelID getNodeQueryLevel(const MultiLevelPartition &, NodeID, LevelID level, CellID) { @@ -65,6 +65,61 @@ inline bool checkParentCellRestriction(CellID cell, LevelID, CellID parent) { return cell == parent; } + +// Unrestricted search with a single phantom node (Args is const PhantomNode &): +// * use partition.GetQueryLevel to find the node query level +// * allow to traverse all cells +template +inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, + const NodeID node, + const PhantomNode &phantom_node) +{ + auto highest_diffrent_level = [&partition, node](const SegmentID &phantom_node) { + if (phantom_node.enabled) + return partition.GetHighestDifferentLevel(phantom_node.id, node); + return INVALID_LEVEL_ID; + }; + + const auto node_level = std::min(highest_diffrent_level(phantom_node.forward_segment_id), + highest_diffrent_level(phantom_node.reverse_segment_id)); + + return node_level; +} + +// Unrestricted search with a single phantom node and a vector of phantom nodes: +// * use partition.GetQueryLevel to find the node query level +// * allow to traverse all cells +template +inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, + NodeID node, + const std::vector &phantom_nodes, + const std::size_t phantom_index, + const std::vector &phantom_indices) +{ + auto min_level = [&partition, node](const PhantomNode &phantom_node) { + + const auto &forward_segment = phantom_node.forward_segment_id; + const auto forward_level = + forward_segment.enabled ? partition.GetHighestDifferentLevel(node, forward_segment.id) + : INVALID_LEVEL_ID; + + const auto &reverse_segment = phantom_node.reverse_segment_id; + const auto reverse_level = + reverse_segment.enabled ? partition.GetHighestDifferentLevel(node, reverse_segment.id) + : INVALID_LEVEL_ID; + + return std::min(forward_level, reverse_level); + }; + + // Get minimum level over all phantoms of the highest different level with respect to node + // This is equivalent to min_{∀ source, target} partition.GetQueryLevel(source, node, target) + auto result = min_level(phantom_nodes[phantom_index]); + for (const auto &index : phantom_indices) + { + result = std::min(result, min_level(phantom_nodes[index])); + } + return result; +} } // Heaps only record for each node its predecessor ("parent") on the shortest path. @@ -74,6 +129,46 @@ inline bool checkParentCellRestriction(CellID cell, LevelID, CellID parent) using PackedEdge = std::tuple; using PackedPath = std::vector; +template +inline void retrievePackedPathFromSingleManyToManyHeap( + const SearchEngineData::ManyToManyQueryHeap &heap, const NodeID middle, OutIter out) +{ + + NodeID current = middle; + NodeID parent = heap.GetData(current).parent; + + while (current != parent) + { + const auto &data = heap.GetData(current); + + if (DIRECTION == FORWARD_DIRECTION) + { + *out = std::make_tuple(parent, current, data.from_clique_arc); + ++out; + } + else if (DIRECTION == REVERSE_DIRECTION) + { + *out = std::make_tuple(current, parent, data.from_clique_arc); + ++out; + } + + current = parent; + parent = heap.GetData(parent).parent; + } +} + +template +inline PackedPath retrievePackedPathFromSingleManyToManyHeap( + const SearchEngineData::ManyToManyQueryHeap &heap, const NodeID middle) +{ + + PackedPath packed_path; + retrievePackedPathFromSingleManyToManyHeap( + heap, middle, std::back_inserter(packed_path)); + + return packed_path; +} + template inline void retrievePackedPathFromSingleHeap(const SearchEngineData::QueryHeap &heap, const NodeID middle, @@ -206,15 +301,22 @@ void relaxOutgoingEdges(const DataFacade &facade, for (const auto edge : facade.GetBorderEdgeRange(level, node)) { const auto &edge_data = facade.GetEdgeData(edge); - if (DIRECTION == FORWARD_DIRECTION ? edge_data.forward : edge_data.backward) + + if ((DIRECTION == FORWARD_DIRECTION) ? facade.IsForwardEdge(edge) + : facade.IsBackwardEdge(edge)) { const NodeID to = facade.GetTarget(edge); if (!facade.ExcludeNode(to) && checkParentCellRestriction(partition.GetCell(level + 1, to), args...)) { - BOOST_ASSERT_MSG(edge_data.weight > 0, "edge_weight invalid"); - const EdgeWeight to_weight = weight + edge_data.weight; + const auto node_weight = + facade.GetNodeWeight(DIRECTION == FORWARD_DIRECTION ? node : to); + const auto turn_penalty = facade.GetWeightPenaltyForEdgeID(edge_data.turn_id); + + // TODO: BOOST_ASSERT(edge_data.weight == node_weight + turn_penalty); + + const EdgeWeight to_weight = weight + node_weight + turn_penalty; if (!forward_heap.WasInserted(to)) { @@ -407,6 +509,90 @@ UnpackedPath search(SearchEngineData &engine_working_data, return std::make_tuple(weight, std::move(unpacked_nodes), std::move(unpacked_edges)); } +// With (s, middle, t) we trace back the paths middle -> s and middle -> t. +// This gives us a packed path (node ids) from the base graph around s and t, +// and overlay node ids otherwise. We then have to unpack the overlay clique +// edges by recursively descending unpacking the path down to the base graph. + +using UnpackedNodes = std::vector; +using UnpackedEdges = std::vector; +using UnpackedPath = std::tuple; + +template +UnpackedPath +unpackPathAndCalculateDistance(SearchEngineData &engine_working_data, + const DataFacade &facade, + typename SearchEngineData::QueryHeap &forward_heap, + typename SearchEngineData::QueryHeap &reverse_heap, + const bool force_loop_forward, + const bool force_loop_reverse, + EdgeWeight weight_upper_bound, + PackedPath packed_path, + NodeID middle, + Args... args) +{ + EdgeWeight weight = weight_upper_bound; + const auto &partition = facade.GetMultiLevelPartition(); + const NodeID source_node = !packed_path.empty() ? std::get<0>(packed_path.front()) : middle; + + // Unpack path + std::vector unpacked_nodes; + std::vector unpacked_edges; + unpacked_nodes.reserve(packed_path.size()); + unpacked_edges.reserve(packed_path.size()); + + unpacked_nodes.push_back(source_node); + + for (auto const &packed_edge : packed_path) + { + NodeID source, target; + bool overlay_edge; + std::tie(source, target, overlay_edge) = packed_edge; + if (!overlay_edge) + { // a base graph edge + unpacked_nodes.push_back(target); + unpacked_edges.push_back(facade.FindEdge(source, target)); + } + else + { // an overlay graph edge + LevelID level = getNodeQueryLevel(partition, source, args...); + CellID parent_cell_id = partition.GetCell(level, source); + BOOST_ASSERT(parent_cell_id == partition.GetCell(level, target)); + + LevelID sublevel = level - 1; + + // Here heaps can be reused, let's go deeper! + forward_heap.Clear(); + reverse_heap.Clear(); + forward_heap.Insert(source, 0, {source}); + reverse_heap.Insert(target, 0, {target}); + + // TODO: when structured bindings will be allowed change to + // auto [subpath_weight, subpath_source, subpath_target, subpath] = ... + EdgeWeight subpath_weight; + std::vector subpath_nodes; + std::vector subpath_edges; + std::tie(subpath_weight, subpath_nodes, subpath_edges) = search(engine_working_data, + facade, + forward_heap, + reverse_heap, + force_loop_forward, + force_loop_reverse, + weight_upper_bound, + sublevel, + parent_cell_id); + BOOST_ASSERT(!subpath_edges.empty()); + BOOST_ASSERT(subpath_nodes.size() > 1); + BOOST_ASSERT(subpath_nodes.front() == source); + BOOST_ASSERT(subpath_nodes.back() == target); + unpacked_nodes.insert( + unpacked_nodes.end(), std::next(subpath_nodes.begin()), subpath_nodes.end()); + unpacked_edges.insert(unpacked_edges.end(), subpath_edges.begin(), subpath_edges.end()); + } + } + return std::make_tuple(weight, std::move(unpacked_nodes), std::move(unpacked_edges)); +} + // Alias to be compatible with the CH-based search template inline void search(SearchEngineData &engine_working_data, diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp index 61e2f1cbfb9..d8e8aeedc88 100644 --- a/include/extractor/edge_based_graph_factory.hpp +++ b/include/extractor/edge_based_graph_factory.hpp @@ -91,6 +91,7 @@ class EdgeBasedGraphFactory void GetEdgeBasedNodeSegments(std::vector &nodes); void GetStartPointMarkers(std::vector &node_is_startpoint); void GetEdgeBasedNodeWeights(std::vector &output_node_weights); + void GetEdgeBasedNodeDurations(std::vector &output_node_durations); std::uint32_t GetConnectivityChecksum() const; std::uint64_t GetNumberOfEdgeBasedNodes() const; @@ -117,6 +118,7 @@ class EdgeBasedGraphFactory //! node weights that indicate the length of the segment (node based) represented by the //! edge-based node std::vector m_edge_based_node_weights; + std::vector m_edge_based_node_durations; //! list of edge based nodes (compressed segments) std::vector m_edge_based_node_segments; diff --git a/include/extractor/extractor.hpp b/include/extractor/extractor.hpp index 0427cfaba98..31b1576586b 100644 --- a/include/extractor/extractor.hpp +++ b/include/extractor/extractor.hpp @@ -87,6 +87,7 @@ class Extractor std::vector &edge_based_node_segments, std::vector &node_is_startpoint, std::vector &edge_based_node_weights, + std::vector &edge_based_node_durations, util::DeallocatingVector &edge_based_edge_list, std::uint32_t &connectivity_checksum); diff --git a/include/extractor/files.hpp b/include/extractor/files.hpp index cb0e87499ef..91f98825af8 100644 --- a/include/extractor/files.hpp +++ b/include/extractor/files.hpp @@ -462,14 +462,28 @@ void readEdgeBasedNodeWeights(const boost::filesystem::path &path, NodeWeigtsVec storage::serialization::read(reader, "/extractor/edge_based_node_weights", weights); } -template -void writeEdgeBasedNodeWeights(const boost::filesystem::path &path, - const NodeWeigtsVectorT &weights) +template +void readEdgeBasedNodeWeightsDurations(const boost::filesystem::path &path, + NodeWeigtsVectorT &weights, + NodeDurationsVectorT &durations) +{ + const auto fingerprint = storage::tar::FileReader::VerifyFingerprint; + storage::tar::FileReader reader{path, fingerprint}; + + storage::serialization::read(reader, "/extractor/edge_based_node_weights", weights); + storage::serialization::read(reader, "/extractor/edge_based_node_durations", durations); +} + +template +void writeEdgeBasedNodeWeightsDurations(const boost::filesystem::path &path, + const NodeWeigtsVectorT &weights, + const NodeDurationsVectorT &durations) { const auto fingerprint = storage::tar::FileWriter::GenerateFingerprint; storage::tar::FileWriter writer{path, fingerprint}; storage::serialization::write(writer, "/extractor/edge_based_node_weights", weights); + storage::serialization::write(writer, "/extractor/edge_based_node_durations", durations); } template @@ -506,29 +520,6 @@ void readCompressedNodeBasedGraph(const boost::filesystem::path &path, EdgeListT storage::serialization::read(reader, "/extractor/cnbg", edge_list); } - -// reads .osrm.maneuver_overrides -template -inline void readManeuverOverrides(const boost::filesystem::path &path, - StorageManeuverOverrideT &maneuver_overrides, - NodeSequencesT &node_sequences) -{ - const auto fingerprint = storage::io::FileReader::VerifyFingerprint; - storage::io::FileReader reader{path, fingerprint}; - - serialization::read(reader, maneuver_overrides, node_sequences); -} - -// writes .osrm.maneuver_overrides -inline void writeManeuverOverrides(const boost::filesystem::path &path, - const std::vector &maneuver_overrides, - const std::vector &node_sequences) -{ - const auto fingerprint = storage::io::FileWriter::GenerateFingerprint; - storage::io::FileWriter writer{path, fingerprint}; - - serialization::write(writer, maneuver_overrides, node_sequences); -} } } } diff --git a/include/nodejs/node_osrm_support.hpp b/include/nodejs/node_osrm_support.hpp index 2bad0a205cd..6ffea408b66 100644 --- a/include/nodejs/node_osrm_support.hpp +++ b/include/nodejs/node_osrm_support.hpp @@ -1064,6 +1064,46 @@ argumentsToTableParameter(const Nan::FunctionCallbackInfo &args, } } + if (obj->Has(Nan::New("annotations").ToLocalChecked())) + { + v8::Local annotations = obj->Get(Nan::New("annotations").ToLocalChecked()); + if (annotations.IsEmpty()) + return table_parameters_ptr(); + + if (!annotations->IsArray()) + { + Nan::ThrowError( + "Annotations must an array containing 'duration' or 'distance', or both"); + return table_parameters_ptr(); + } + + params->annotations = osrm::TableParameters::AnnotationsType::None; + + v8::Local annotations_array = v8::Local::Cast(annotations); + for (std::size_t i = 0; i < annotations_array->Length(); ++i) + { + const Nan::Utf8String annotations_utf8str(annotations_array->Get(i)); + std::string annotations_str{*annotations_utf8str, + *annotations_utf8str + annotations_utf8str.length()}; + + if (annotations_str == "duration") + { + params->annotations = + params->annotations | osrm::TableParameters::AnnotationsType::Duration; + } + else if (annotations_str == "distance") + { + params->annotations = + params->annotations | osrm::TableParameters::AnnotationsType::Distance; + } + else + { + Nan::ThrowError("this 'annotations' param is not supported"); + return table_parameters_ptr(); + } + } + } + return params; } diff --git a/include/partitioner/files.hpp b/include/partitioner/files.hpp index 3a85a8fb0f7..30bd2942d48 100644 --- a/include/partitioner/files.hpp +++ b/include/partitioner/files.hpp @@ -1,8 +1,6 @@ #ifndef OSRM_PARTITIONER_SERILIZATION_HPP #define OSRM_PARTITIONER_SERILIZATION_HPP -#include "customizer/edge_based_graph.hpp" - #include "partitioner/serialization.hpp" #include "storage/io.hpp" @@ -14,39 +12,6 @@ namespace partitioner namespace files { -// reads .osrm.mldgr file -template -inline void readGraph(const boost::filesystem::path &path, - MultiLevelGraphT &graph, - std::uint32_t &connectivity_checksum) -{ - static_assert(std::is_same::value || - std::is_same::value, - ""); - - storage::tar::FileReader reader{path, storage::tar::FileReader::VerifyFingerprint}; - - reader.ReadInto("/mld/connectivity_checksum", connectivity_checksum); - serialization::read(reader, "/mld/multilevelgraph", graph); -} - -// writes .osrm.mldgr file -template -inline void writeGraph(const boost::filesystem::path &path, - const MultiLevelGraphT &graph, - const std::uint32_t connectivity_checksum) -{ - static_assert(std::is_same::value || - std::is_same::value, - ""); - - storage::tar::FileWriter writer{path, storage::tar::FileWriter::GenerateFingerprint}; - - writer.WriteElementCount64("/mld/connectivity_checksum", 1); - writer.WriteFrom("/mld/connectivity_checksum", connectivity_checksum); - serialization::write(writer, "/mld/multilevelgraph", graph); -} - // read .osrm.partition file template inline void readPartition(const boost::filesystem::path &path, MultiLevelPartitionT &mlp) @@ -102,6 +67,35 @@ inline void writeCells(const boost::filesystem::path &path, CellStorageT &storag serialization::write(writer, "/mld/cellstorage", storage); } + +// reads .osrm.mldgr file +template +inline void readGraph(const boost::filesystem::path &path, + MultiLevelGraphT &graph, + std::uint32_t &connectivity_checksum) +{ + static_assert(std::is_same::value, ""); + + storage::tar::FileReader reader{path, storage::tar::FileReader::VerifyFingerprint}; + + reader.ReadInto("/mld/connectivity_checksum", connectivity_checksum); + serialization::read(reader, "/mld/multilevelgraph", graph); +} + +// writes .osrm.mldgr file +template +inline void writeGraph(const boost::filesystem::path &path, + const MultiLevelGraphT &graph, + const std::uint32_t connectivity_checksum) +{ + static_assert(std::is_same::value, ""); + + storage::tar::FileWriter writer{path, storage::tar::FileWriter::GenerateFingerprint}; + + writer.WriteElementCount64("/mld/connectivity_checksum", 1); + writer.WriteFrom("/mld/connectivity_checksum", connectivity_checksum); + serialization::write(writer, "/mld/multilevelgraph", graph); +} } } } diff --git a/include/partitioner/multi_level_graph.hpp b/include/partitioner/multi_level_graph.hpp index b652589f62a..2d563579025 100644 --- a/include/partitioner/multi_level_graph.hpp +++ b/include/partitioner/multi_level_graph.hpp @@ -1,6 +1,7 @@ #ifndef OSRM_PARTITIONER_MULTI_LEVEL_GRAPH_HPP #define OSRM_PARTITIONER_MULTI_LEVEL_GRAPH_HPP +#include "partitioner/edge_based_graph.hpp" #include "partitioner/multi_level_partition.hpp" #include "storage/shared_memory_ownership.hpp" @@ -42,6 +43,8 @@ class MultiLevelGraph : public util::StaticGraph template using Vector = util::ViewOrVector; public: + using SuperT::SuperT; + // We limit each node to have 255 edges // this is very generous, we could probably pack this using EdgeOffset = std::uint8_t; @@ -146,6 +149,14 @@ class MultiLevelGraph : public util::StaticGraph return max_border_node_id; } + auto data() && // rvalue ref-qualifier is a safety-belt + { + return std::make_tuple(std::move(SuperT::node_array), + std::move(SuperT::edge_array), + std::move(node_to_edge_offset), + connectivity_checksum); + } + private: template auto GetHighestBorderLevel(const MultiLevelPartition &mlp, const ContainerT &edges) const @@ -218,9 +229,13 @@ class MultiLevelGraph : public util::StaticGraph const std::string &name, const MultiLevelGraph &graph); + protected: Vector node_to_edge_offset; std::uint32_t connectivity_checksum; }; + +using MultiLevelEdgeBasedGraph = + MultiLevelGraph; } } diff --git a/include/partitioner/partitioner_config.hpp b/include/partitioner/partitioner_config.hpp index 31423c919bd..04809dff457 100644 --- a/include/partitioner/partitioner_config.hpp +++ b/include/partitioner/partitioner_config.hpp @@ -16,7 +16,7 @@ namespace partitioner struct PartitionerConfig final : storage::IOConfig { PartitionerConfig() - : IOConfig({".osrm", ".osrm.fileIndex", ".osrm.ebg_nodes"}, + : IOConfig({".osrm", ".osrm.fileIndex", ".osrm.ebg_nodes", ".osrm.enw"}, {".osrm.hsgr", ".osrm.cnbg"}, {".osrm.ebg", ".osrm.cnbg", diff --git a/include/partitioner/serialization.hpp b/include/partitioner/serialization.hpp index b8d57bb6dbc..6f43c02eaeb 100644 --- a/include/partitioner/serialization.hpp +++ b/include/partitioner/serialization.hpp @@ -19,26 +19,6 @@ namespace partitioner namespace serialization { -template -inline void read(storage::tar::FileReader &reader, - const std::string &name, - MultiLevelGraph &graph) -{ - storage::serialization::read(reader, name + "/node_array", graph.node_array); - storage::serialization::read(reader, name + "/edge_array", graph.edge_array); - storage::serialization::read(reader, name + "/node_to_edge_offset", graph.node_to_edge_offset); -} - -template -inline void write(storage::tar::FileWriter &writer, - const std::string &name, - const MultiLevelGraph &graph) -{ - storage::serialization::write(writer, name + "/node_array", graph.node_array); - storage::serialization::write(writer, name + "/edge_array", graph.edge_array); - storage::serialization::write(writer, name + "/node_to_edge_offset", graph.node_to_edge_offset); -} - template inline void read(storage::tar::FileReader &reader, const std::string &name, diff --git a/include/server/api/table_parameter_grammar.hpp b/include/server/api/table_parameter_grammar.hpp index 5a97c8e3d40..5be79364501 100644 --- a/include/server/api/table_parameter_grammar.hpp +++ b/include/server/api/table_parameter_grammar.hpp @@ -22,11 +22,11 @@ namespace qi = boost::spirit::qi; template -struct TableParametersGrammar final : public BaseParametersGrammar +struct TableParametersGrammar : public BaseParametersGrammar { using BaseGrammar = BaseParametersGrammar; - TableParametersGrammar() : BaseGrammar(root_rule) + TableParametersGrammar() : TableParametersGrammar(root_rule) { #ifdef BOOST_HAS_LONG_LONG if (std::is_same::value) @@ -51,15 +51,35 @@ struct TableParametersGrammar final : public BaseParametersGrammar -qi::lit(".json") > - -('?' > (table_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&'); + -('?' > (table_rule(qi::_r1) | base_rule(qi::_r1)) % '&'); } + TableParametersGrammar(qi::rule &root_rule_) : BaseGrammar(root_rule_) + { + using AnnotationsType = engine::api::TableParameters::AnnotationsType; + + annotations.add("duration", AnnotationsType::Duration)("distance", + AnnotationsType::Distance); + + annotations_list = annotations[qi::_val |= qi::_1] % ','; + + base_rule = BaseGrammar::base_rule(qi::_r1) | + (qi::lit("annotations=") > + annotations_list[ph::bind(&engine::api::TableParameters::annotations, + qi::_r1) = qi::_1]); + } + + protected: + qi::rule base_rule; + private: qi::rule root_rule; qi::rule table_rule; qi::rule sources_rule; qi::rule destinations_rule; qi::rule size_t_; + qi::symbols annotations; + qi::rule annotations_list; }; } } diff --git a/include/storage/shared_data_index.hpp b/include/storage/shared_data_index.hpp index 0c6bd79423f..94f78182ac7 100644 --- a/include/storage/shared_data_index.hpp +++ b/include/storage/shared_data_index.hpp @@ -46,33 +46,39 @@ class SharedDataIndex template auto GetBlockPtr(const std::string &name) const { - const auto index_iter = block_to_region.find(name); - const auto ®ion = regions[index_iter->second]; + const auto ®ion = GetBlockRegion(name); return region.layout.GetBlockPtr(region.memory_ptr, name); } template auto GetBlockPtr(const std::string &name) { - const auto index_iter = block_to_region.find(name); - const auto ®ion = regions[index_iter->second]; + const auto ®ion = GetBlockRegion(name); return region.layout.GetBlockPtr(region.memory_ptr, name); } std::size_t GetBlockEntries(const std::string &name) const { - const auto index_iter = block_to_region.find(name); - const auto ®ion = regions[index_iter->second]; + const auto ®ion = GetBlockRegion(name); return region.layout.GetBlockEntries(name); } std::size_t GetBlockSize(const std::string &name) const { - const auto index_iter = block_to_region.find(name); - const auto ®ion = regions[index_iter->second]; + const auto ®ion = GetBlockRegion(name); return region.layout.GetBlockSize(name); } private: + const AllocatedRegion &GetBlockRegion(const std::string &name) const + { + const auto index_iter = block_to_region.find(name); + if (index_iter == block_to_region.end()) + { + throw util::exception("data block " + name + " not found " + SOURCE_REF); + } + return regions[index_iter->second]; + } + std::vector regions; std::unordered_map block_to_region; }; diff --git a/include/storage/view_factory.hpp b/include/storage/view_factory.hpp index cf6839a050a..f6c8e213739 100644 --- a/include/storage/view_factory.hpp +++ b/include/storage/view_factory.hpp @@ -330,9 +330,18 @@ inline auto make_multi_level_graph_view(const SharedDataIndex &index, const std: index, name + "/edge_array"); auto node_to_offset = make_vector_view( index, name + "/node_to_edge_offset"); - - return customizer::MultiLevelEdgeBasedGraphView( - std::move(node_list), std::move(edge_list), std::move(node_to_offset)); + auto node_weights = make_vector_view(index, name + "/node_weights"); + auto node_durations = make_vector_view(index, name + "/node_durations"); + auto is_forward_edge = make_vector_view(index, name + "/is_forward_edge"); + auto is_backward_edge = make_vector_view(index, name + "/is_backward_edge"); + + return customizer::MultiLevelEdgeBasedGraphView(std::move(node_list), + std::move(edge_list), + std::move(node_to_offset), + std::move(node_weights), + std::move(node_durations), + std::move(is_forward_edge), + std::move(is_backward_edge)); } inline auto make_maneuver_overrides_views(const SharedDataIndex &index, const std::string &name) diff --git a/include/updater/updater.hpp b/include/updater/updater.hpp index 08b451f0c1f..dcaf5f638a8 100644 --- a/include/updater/updater.hpp +++ b/include/updater/updater.hpp @@ -17,13 +17,15 @@ class Updater public: Updater(UpdaterConfig config_) : config(std::move(config_)) {} - using NumNodesAndEdges = - std::tuple, std::uint32_t>; - NumNodesAndEdges LoadAndUpdateEdgeExpandedGraph() const; + EdgeID + LoadAndUpdateEdgeExpandedGraph(std::vector &edge_based_edge_list, + std::vector &node_weights, + std::uint32_t &connectivity_checksum) const; EdgeID LoadAndUpdateEdgeExpandedGraph(std::vector &edge_based_edge_list, std::vector &node_weights, + std::vector &node_durations, // TODO: to be deleted std::uint32_t &connectivity_checksum) const; private: diff --git a/include/updater/updater_config.hpp b/include/updater/updater_config.hpp index cd41bac54db..64306a609b4 100644 --- a/include/updater/updater_config.hpp +++ b/include/updater/updater_config.hpp @@ -53,7 +53,8 @@ struct UpdaterConfig final : storage::IOConfig ".osrm.geometry", ".osrm.fileIndex", ".osrm.properties", - ".osrm.restrictions"}, + ".osrm.restrictions", + ".osrm.enw"}, {}, {".osrm.datasource_names"}), valid_now(0) diff --git a/include/util/coordinate_calculation.hpp b/include/util/coordinate_calculation.hpp index aa60504ed8b..c946842a23e 100644 --- a/include/util/coordinate_calculation.hpp +++ b/include/util/coordinate_calculation.hpp @@ -43,6 +43,9 @@ inline double radToDeg(const double radian) //! Takes the squared euclidean distance of the input coordinates. Does not return meters! std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rhs); +double fccApproximateDistance(const Coordinate first_coordinate, + const Coordinate second_coordinate); + double haversineDistance(const Coordinate first_coordinate, const Coordinate second_coordinate); double greatCircleDistance(const Coordinate first_coordinate, const Coordinate second_coordinate); diff --git a/include/util/debug.hpp b/include/util/debug.hpp index c1ed8344294..9c6582dad2b 100644 --- a/include/util/debug.hpp +++ b/include/util/debug.hpp @@ -22,7 +22,7 @@ namespace util inline std::ostream &operator<<(std::ostream &out, const Coordinate &coordinate) { out << std::setprecision(12) << "{" << toFloating(coordinate.lon) << ", " - << toFloating(coordinate.lon) << "}"; + << toFloating(coordinate.lat) << "}"; return out; } } diff --git a/include/util/dynamic_graph.hpp b/include/util/dynamic_graph.hpp index 6fd599759e3..2a22aeae7c8 100644 --- a/include/util/dynamic_graph.hpp +++ b/include/util/dynamic_graph.hpp @@ -413,6 +413,11 @@ template class DynamicGraph util::inplacePermutation(node_array.begin(), node_array.end(), old_to_new_node); // Build up edge permutation + if (edge_list.size() >= std::numeric_limits::max()) + { + throw util::exception("There are too many edges, OSRM only supports 2^32" + SOURCE_REF); + } + EdgeID new_edge_index = 0; std::vector old_to_new_edge(edge_list.size(), SPECIAL_EDGEID); for (auto node : util::irange(0, number_of_nodes)) @@ -421,11 +426,6 @@ template class DynamicGraph // move all filled edges for (auto edge : GetAdjacentEdgeRange(node)) { - if (new_edge_index == std::numeric_limits::max()) - { - throw util::exception("There are too many edges, OSRM only supports 2^32" + - SOURCE_REF); - } edge_list[edge].target = old_to_new_node[edge_list[edge].target]; BOOST_ASSERT(edge_list[edge].target != SPECIAL_NODEID); old_to_new_edge[edge] = new_edge_index++; diff --git a/include/util/query_heap.hpp b/include/util/query_heap.hpp index e547ac9735d..f4b9e626ab0 100644 --- a/include/util/query_heap.hpp +++ b/include/util/query_heap.hpp @@ -226,12 +226,14 @@ class QueryHeap Data &GetData(NodeID node) { const auto index = node_index.peek_index(node); + BOOST_ASSERT((int)index >= 0 && (int)index < (int)inserted_nodes.size()); return inserted_nodes[index].data; } Data const &GetData(NodeID node) const { const auto index = node_index.peek_index(node); + BOOST_ASSERT((int)index >= 0 && (int)index < (int)inserted_nodes.size()); return inserted_nodes[index].data; } diff --git a/include/util/static_graph.hpp b/include/util/static_graph.hpp index 433a830c1a5..6092dd955a2 100644 --- a/include/util/static_graph.hpp +++ b/include/util/static_graph.hpp @@ -312,7 +312,7 @@ class StaticGraph }); } - // private: + protected: NodeIterator number_of_nodes; EdgeIterator number_of_edges; diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp index 9b8ecc82bbc..f0104e7b258 100644 --- a/include/util/typedefs.hpp +++ b/include/util/typedefs.hpp @@ -75,6 +75,7 @@ using NameID = std::uint32_t; using AnnotationID = std::uint32_t; using EdgeWeight = std::int32_t; using EdgeDuration = std::int32_t; +using EdgeDistance = float; using SegmentWeight = std::uint32_t; using SegmentDuration = std::uint32_t; using TurnPenalty = std::int16_t; // turn penalty in 100ms units @@ -113,6 +114,7 @@ static const SegmentDuration MAX_SEGMENT_DURATION = INVALID_SEGMENT_DURATION - 1 static const EdgeWeight INVALID_EDGE_WEIGHT = std::numeric_limits::max(); static const EdgeDuration MAXIMAL_EDGE_DURATION = std::numeric_limits::max(); static const TurnPenalty INVALID_TURN_PENALTY = std::numeric_limits::max(); +static const EdgeDistance INVALID_EDGE_DISTANCE = std::numeric_limits::max(); // FIXME the bitfields we use require a reduced maximal duration, this should be kept consistent // within the code base. For now we have to ensure that we don't case 30 bit to -1 and break any diff --git a/include/util/vector_tile.hpp b/include/util/vector_tile.hpp index f3c05791ef7..d3d4978b11a 100644 --- a/include/util/vector_tile.hpp +++ b/include/util/vector_tile.hpp @@ -9,31 +9,6 @@ namespace util { namespace vector_tile { - -const constexpr std::uint32_t ID_TAG = 1; -const constexpr std::uint32_t NAME_TAG = 1; -const constexpr std::uint32_t FEATURE_TAG = 2; -const constexpr std::uint32_t LAYER_TAG = 3; -const constexpr std::uint32_t GEOMETRY_TAG = 3; -const constexpr std::uint32_t KEY_TAG = 3; -const constexpr std::uint32_t VARIANT_TAG = 4; -const constexpr std::uint32_t EXTENT_TAG = 5; -const constexpr std::uint32_t VERSION_TAG = 15; - -const constexpr std::uint32_t FEATURE_ATTRIBUTES_TAG = 2; -const constexpr std::uint32_t FEATURE_GEOMETRIES_TAG = 4; - -const constexpr std::uint32_t GEOMETRY_TYPE_POINT = 1; -const constexpr std::uint32_t GEOMETRY_TYPE_LINE = 2; - -const constexpr std::uint32_t VARIANT_TYPE_STRING = 1; -const constexpr std::uint32_t VARIANT_TYPE_FLOAT = 2; -const constexpr std::uint32_t VARIANT_TYPE_DOUBLE = 3; - -const constexpr std::uint32_t VARIANT_TYPE_UINT64 = 5; -const constexpr std::uint32_t VARIANT_TYPE_SINT64 = 6; -const constexpr std::uint32_t VARIANT_TYPE_BOOL = 7; - // Vector tiles are 4096 virtual pixels on each side const constexpr double EXTENT = 4096.0; const constexpr double BUFFER = 128.0; diff --git a/package.json b/package.json index 28acff87ec4..29ef7bca01a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "osrm", - "version": "5.17.3", + "version": "5.18.0", "private": false, "description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.", "dependencies": { @@ -38,19 +38,25 @@ "node": ">=4.0.0" }, "devDependencies": { + "ansi-escape-sequences": "^4.0.0", "aws-sdk": "~2.0.31", "babel-plugin-transform-class-properties": "^6.24.1", "chalk": "^1.1.3", + "command-line-args": "^5.0.2", + "command-line-usage": "^5.0.4", + "csv-stringify": "^3.0.0", "cucumber": "^1.2.1", "d3-queue": "^2.0.3", "docbox": "^1.0.6", "documentation": "^4.0.0-rc.1", "eslint": "^2.4.0", "faucet": "^0.0.1", + "jsonpath": "^1.0.0", "node-timeout": "0.0.4", "polyline": "^0.2.0", "request": "^2.69.0", "tape": "^4.7.0", + "turf": "^3.0.14", "xmlbuilder": "^4.2.1" }, "bundleDependencies": [ diff --git a/profiles/bicycle.lua b/profiles/bicycle.lua index 2641526a6ce..f08bcebe26d 100644 --- a/profiles/bicycle.lua +++ b/profiles/bicycle.lua @@ -193,6 +193,16 @@ function setup() sett = 10 }, + classes = Sequence { + 'ferry', 'tunnel' + }, + + -- Which classes should be excludable + -- This increases memory usage so its disabled by default. + excludable = Sequence { +-- Set {'ferry'} + }, + tracktype_speeds = { }, @@ -650,6 +660,9 @@ function process_way(profile, way, result) -- set name, ref and pronunciation WayHandlers.names, + -- set classes + WayHandlers.classes, + -- set weight properties of the way WayHandlers.weights } diff --git a/profiles/lib/way_handlers.lua b/profiles/lib/way_handlers.lua index 68e2706d9bc..ef9fa88e8c0 100644 --- a/profiles/lib/way_handlers.lua +++ b/profiles/lib/way_handlers.lua @@ -306,37 +306,46 @@ end -- add class information function WayHandlers.classes(profile,way,result,data) + if not profile.classes then + return + end + + local allowed_classes = Set {} + for k, v in pairs(profile.classes) do + allowed_classes[v] = true + end + local forward_toll, backward_toll = Tags.get_forward_backward_by_key(way, data, "toll") local forward_route, backward_route = Tags.get_forward_backward_by_key(way, data, "route") local tunnel = way:get_value_by_key("tunnel") - if tunnel and tunnel ~= "no" then + if allowed_classes["tunnel"] and tunnel and tunnel ~= "no" then result.forward_classes["tunnel"] = true result.backward_classes["tunnel"] = true end - if forward_toll == "yes" then + if allowed_classes["toll"] and forward_toll == "yes" then result.forward_classes["toll"] = true end - if backward_toll == "yes" then + if allowed_classes["toll"] and backward_toll == "yes" then result.backward_classes["toll"] = true end - if forward_route == "ferry" then + if allowed_classes["ferry"] and forward_route == "ferry" then result.forward_classes["ferry"] = true end - if backward_route == "ferry" then + if allowed_classes["ferry"] and backward_route == "ferry" then result.backward_classes["ferry"] = true end - if result.forward_restricted then + if allowed_classes["restricted"] and result.forward_restricted then result.forward_classes["restricted"] = true end - if result.backward_restricted then + if allowed_classes["restricted"] and result.backward_restricted then result.backward_classes["restricted"] = true end - if data.highway == "motorway" or data.highway == "motorway_link" then + if allowed_classes["motorway"] and (data.highway == "motorway" or data.highway == "motorway_link") then result.forward_classes["motorway"] = true result.backward_classes["motorway"] = true end diff --git a/profiles/testbot.lua b/profiles/testbot.lua index d4288bd8137..691e5761772 100644 --- a/profiles/testbot.lua +++ b/profiles/testbot.lua @@ -5,7 +5,7 @@ -- Secondary road: 18km/h = 18000m/3600s = 100m/20s -- Tertiary road: 12km/h = 12000m/3600s = 100m/30s -api_version = 3 +api_version = 4 function setup() return { @@ -14,7 +14,7 @@ function setup() max_speed_for_map_matching = 30/3.6, --km -> m/s weight_name = 'duration', process_call_tagless_node = false, - uturn_penalty = 20, + u_turn_penalty = 20, traffic_light_penalty = 7, -- seconds use_turn_restrictions = true }, @@ -32,7 +32,7 @@ function setup() primary = 36, secondary = 18, tertiary = 12, - steps = 6, + steps = 6 } } end @@ -128,9 +128,9 @@ function process_way (profile, way, result) end function process_turn (profile, turn) - if turn.direction_modifier == direction_modifier.uturn then - turn.duration = profile.properties.uturn_penalty - turn.weight = profile.properties.uturn_penalty + if turn.is_u_turn then + turn.duration = turn.duration + profile.properties.u_turn_penalty + turn.weight = turn.weight + profile.properties.u_turn_penalty end if turn.has_traffic_light then turn.duration = turn.duration + profile.properties.traffic_light_penalty diff --git a/scripts/gdb_printers.py b/scripts/gdb_printers.py index ca9568c8341..f9f5062d2c9 100644 --- a/scripts/gdb_printers.py +++ b/scripts/gdb_printers.py @@ -9,6 +9,8 @@ def call(this, method, *args): """Call this.method(args)""" + if (str(this) == ''): + raise BaseException('"this" is optimized out') command = '(*({})({})).{}({})'.format(this.type.target().pointer(), this.address, method, ','.join((str(x) for x in args))) return gdb.parse_and_eval(command) @@ -234,7 +236,6 @@ def Facade(facade, width, height, arg): mld_facade = facade.cast(gdb.lookup_type('osrm::engine::datafacade::ContiguousInternalMemoryAlgorithmDataFacade')) mld_partition = mld_facade['mld_partition'] mld_levels = call(mld_partition, 'GetNumberOfLevels') - print (mld_level, mld_levels) if mld_level < mld_levels: sentinel_node = call(mld_partition['partition'], 'size') - 1 # GetSentinelNode number_of_cells = call(mld_partition, 'GetCell', mld_level, sentinel_node) # GetNumberOfCells @@ -272,6 +273,7 @@ def Facade(facade, width, height, arg): for node in nodes: geometry_id = call(facade, 'GetGeometryIndex', node) direction = 'forward' if geometry_id['forward'] else 'reverse' + print (geometry_id, direction) geometry = SVGPrinter.getByGeometryId(facade, geometry_id, 'Geometry') weights = SVGPrinter.getByGeometryId(facade, geometry_id, 'Weights') diff --git a/scripts/osrm-runner.js b/scripts/osrm-runner.js index 7f22d0576d1..76486496910 100755 --- a/scripts/osrm-runner.js +++ b/scripts/osrm-runner.js @@ -10,13 +10,12 @@ const clu = require('command-line-usage'); const ansi = require('ansi-escape-sequences'); const turf = require('turf'); const jp = require('jsonpath'); +const csv_stringify = require('csv-stringify/lib/sync'); const run_query = (query_options, filters, callback) => { let tic = () => 0.; http.request(query_options, function (res) { let body = '', ttfb = tic(); - if (res.statusCode != 200) - return callback(query_options.path, res.statusCode, ttfb); res.setEncoding('utf8'); res.on('data', function (chunk) { @@ -35,27 +34,51 @@ const run_query = (query_options, filters, callback) => { }).end(); }; -function generate_points(polygon, number) { +function generate_points(polygon, number, coordinates_number, max_distance) { let query_points = []; while (query_points.length < number) { - var chunk = turf - .random('points', number, { bbox: turf.bbox(polygon)}) - .features - .map(x => x.geometry.coordinates) - .filter(pt => turf.inside(pt, polygon)); - query_points = query_points.concat(chunk); + let points = []; + + while(points.length < coordinates_number) { + let chunk = turf + .random('points', coordinates_number, { bbox: turf.bbox(polygon)}) + .features + .map(x => x.geometry.coordinates) + .filter(pt => turf.inside(pt, polygon)); + + if (max_distance > 0) + { + chunk.forEach(pt => { + if (points.length == 0) + { + points.push(pt); + } + else + { + let distance = turf.distance(pt, points[points.length-1], 'meters'); + if (distance < max_distance) + { + points.push(pt); + } + } + }); + } + else + { + points = points.concat(chunk); + } + } + + query_points.push(points); } - return query_points.slice(0, number); + + return query_points; } -function generate_queries(options, query_points, coordinates_number) { - let queries = []; - for (let chunk = 0; chunk < query_points.length; chunk += coordinates_number) - { - let points = query_points.slice(chunk, chunk + coordinates_number); - let query = options.path.replace(/{}/g, x => points.pop().join(',')); - queries.push(query); - } +function generate_queries(options, query_points) { + let queries = query_points.map(points => { + return options.path.replace(/{}/g, x => points.pop().join(',')); + }); return queries; } @@ -73,27 +96,22 @@ function BoundingBox(x) { } const optionsList = [ {name: 'help', alias: 'h', type: Boolean, description: 'Display this usage guide.', defaultValue: false}, - {name: 'server', alias: 's', type: ServerDetails, defaultValue: ServerDetails('localhost:5000'), - description: 'OSRM routing server', typeLabel: '[underline]{hostname[:port]}'}, - {name: 'path', alias: 'p', type: String, defaultValue: '/route/v1/driving/{};{}', - description: 'OSRM query path with {} coordinate placeholders, default /route/v1/driving/{};{}', typeLabel: '[underline]{path}'}, - {name: 'filter', alias: 'f', type: String, defaultValue: ['$.routes[0].weight'], multiple: true, - description: 'JSONPath filters, default "$.routes[0].weight"', typeLabel: '[underline]{filter}'}, - {name: 'bounding-box', alias: 'b', type: BoundingBox, defaultValue: BoundingBox('5.86442,47.2654,15.0508,55.1478'), multiple: true, - description: 'queries bounding box, default "5.86442,47.2654,15.0508,55.1478"', typeLabel: '[underline]{west,south,east,north}'}, - {name: 'max-sockets', alias: 'm', type: Number, defaultValue: 1, - description: 'how many concurrent sockets the agent can have open per origin, default 1', typeLabel: '[underline]{number}'}, - {name: 'number', alias: 'n', type: Number, defaultValue: 10, - description: 'number of query points, default 10', typeLabel: '[underline]{number}'}, - {name: 'queries-files', alias: 'q', type: String, - description: 'CSV file with queries in the first row', typeLabel: '[underline]{file}'}]; + {name: 'server', alias: 's', type: ServerDetails, defaultValue: ServerDetails('localhost:5000'), description: 'OSRM routing server', typeLabel: '{underline hostname[:port]}'}, + {name: 'path', alias: 'p', type: String, defaultValue: '/route/v1/driving/{};{}', description: 'OSRM query path with \\{\\} coordinate placeholders, default /route/v2/driving/\\{\\};\\{\\}', typeLabel: '{underline path}'}, + {name: 'filter', alias: 'f', type: String, defaultValue: ['$.routes[0].weight'], multiple: true, description: 'JSONPath filters, default "$.routes[0].weight"', typeLabel: '{underline filter}'}, + {name: 'bounding-box', alias: 'b', type: BoundingBox, defaultValue: BoundingBox('5.86442,47.2654,15.0508,55.1478'), multiple: true, description: 'queries bounding box, default "5.86442,47.2654,15.0508,55.1478"', typeLabel: '{underline west,south,east,north}'}, + {name: 'max-sockets', alias: 'm', type: Number, defaultValue: 1, description: 'how many concurrent sockets the agent can have open per origin, default 1', typeLabel: '{underline number}'}, + {name: 'number', alias: 'n', type: Number, defaultValue: 10, description: 'number of query points, default 10', typeLabel: '{underline number}'}, + {name: 'max-distance', alias: 'd', type: Number, defaultValue: -1, description: 'maximal distance between coordinates', typeLabel: '{underline number}'}, + {name: 'queries-files', alias: 'q', type: String, description: 'CSV file with queries in the first row', typeLabel: '{underline file}'}, +]; const options = cla(optionsList); if (options.help) { - const banner = - String.raw` ____ _______ __ ___ ___ __ ___ ___ _________ ` + '\n' + - String.raw` / __ \/ __/ _ \/ |/ / / _ \/ / / / |/ / |/ / __/ _ \ ` + '\n' + - String.raw`/ /_/ /\ \/ , _/ /|_/ / / , _/ /_/ / / / _// , _/ ` + '\n' + - String.raw`\____/___/_/|_/_/ /_/ /_/|_|\____/_/|_/_/|_/___/_/|_| `; + const banner =`\ + ____ _______ __ ______ __ ___ ___ _________ + / __ \\\\/ __/ _ \\\\/ |/ / _ \\\\/ / / / |/ / |/ / __/ _ \\\\ +/ /_/ /\\\\ \\\\/ , _/ /|_/ / , _/ /_/ / / / _// , _/ +\\\\____/___/_/|_/_/ /_/_/|_|\\\\____/_/|_/_/|_/___/_/|_|`; const usage = clu([ { content: ansi.format(banner, 'green'), raw: true }, { header: 'Run OSRM queries and collect results'/*, content: 'Generates something [italic]{very} important.'*/ }, @@ -114,8 +132,8 @@ if (options.hasOwnProperty('queries-files')) { } else { const polygon = options['bounding-box'].map(x => x.poly).reduce((x,y) => turf.union(x, y)); const coordinates_number = (options.path.match(/{}/g) || []).length; - const query_points = generate_points(polygon, coordinates_number * options.number); - queries = generate_queries(options, query_points, coordinates_number); + const query_points = generate_points(polygon, options.number, coordinates_number, options['max-distance']); + queries = generate_queries(options, query_points); } queries = queries.map(q => { return {hostname: options.server.hostname, port: options.server.port, path: q}; }); @@ -123,11 +141,8 @@ queries = queries.map(q => { return {hostname: options.server.hostname, port: op http.globalAgent.maxSockets = options['max-sockets']; queries.map(query => { run_query(query, options.filter, (query, code, ttfb, total, results) => { - let str = `"${query}",${code}`; - if (ttfb !== undefined) str += `,${ttfb}`; - if (total !== undefined) str += `,${total}`; - if (typeof results === 'object' && results.length > 0) - str += ',' + results.map(x => isNaN(x) ? '"' + JSON.stringify(x).replace(/\n/g, ';').replace(/"/g, "'") + '"' : Number(x)).join(','); - console.log(str); + let data = results ? JSON.stringify(results[0]) : ''; + let record = [[query, code, ttfb, total, data]]; + process.stdout.write(csv_stringify(record)); }); }); diff --git a/scripts/update_dependencies.sh b/scripts/update_dependencies.sh index 0b7f33ec69a..57dd62659d5 100755 --- a/scripts/update_dependencies.sh +++ b/scripts/update_dependencies.sh @@ -9,79 +9,50 @@ set -o nounset # structure will be lost. # http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html -OSMIUM_REPO="https://github.com/osmcode/libosmium.git" -OSMIUM_TAG=v2.13.1 +OSMIUM_PATH="osmcode/libosmium" +OSMIUM_TAG=v2.14.0 -VARIANT_REPO="https://github.com/mapbox/variant.git" +VARIANT_PATH="mapbox/variant" VARIANT_TAG=v1.1.3 -SOL_REPO="https://github.com/ThePhD/sol2.git" +SOL_PATH="ThePhD/sol2" SOL_TAG=v2.17.5 -RAPIDJSON_REPO="https://github.com/miloyip/rapidjson.git" +RAPIDJSON_PATH="Tencent/rapidjson" RAPIDJSON_TAG=v1.1.0 -MICROTAR_REPO="https://github.com/rxi/microtar" +MICROTAR_PATH="rxi/microtar" MICROTAR_TAG=v0.1.0 -VARIANT_LATEST=$(curl "https://api.github.com/repos/mapbox/variant/releases/latest" | jq ".tag_name") -OSMIUM_LATEST=$(curl "https://api.github.com/repos/osmcode/libosmium/releases/latest" | jq ".tag_name") -SOL_LATEST=$(curl "https://api.github.com/repos/ThePhD/sol2/releases/latest" | jq ".tag_name") -RAPIDJSON_LATEST=$(curl "https://api.github.com/repos/miloyip/rapidjson/releases/latest" | jq ".tag_name") -MICROTAR_LATEST=$(curl "https://api.github.com/repos/rxi/microtar/releases/latest" | jq ".tag_name") - -echo "Latest osmium release is $OSMIUM_LATEST, pulling in \"$OSMIUM_TAG\"" -echo "Latest variant release is $VARIANT_LATEST, pulling in \"$VARIANT_TAG\"" -echo "Latest sol2 release is $SOL_LATEST, pulling in \"$SOL_TAG\"" -echo "Latest rapidjson release is $RAPIDJSON_LATEST, pulling in \"$RAPIDJSON_TAG\"" -echo "Latest microtar release is $MICROTAR_LATEST, pulling in \"$MICROTAR_TAG\"" - -read -p "Update osmium (y/n) " ok -if [[ $ok =~ [yY] ]] -then - if [ -d "third_party/libosmium" ]; then - git subtree pull -P third_party/libosmium/ $OSMIUM_REPO $OSMIUM_TAG --squash - else - git subtree add -P third_party/libosmium/ $OSMIUM_REPO $OSMIUM_TAG --squash - fi -fi - -read -p "Update variant (y/n) " ok -if [[ $ok =~ [yY] ]] -then - if [ -d "third_party/variant" ]; then - git subtree pull -P third_party/variant/ $VARIANT_REPO $VARIANT_TAG --squash - else - git subtree add -P third_party/variant/ $VARIANT_REPO $VARIANT_TAG --squash - fi -fi - -read -p "Update sol2 (y/n) " ok -if [[ $ok =~ [yY] ]] -then - if [ -d "third_party/sol2" ]; then - git subtree pull -P third_party/sol2/sol2/ $SOL_REPO $SOL_TAG --squash - else - git subtree add -P third_party/sol2/sol2/ $SOL_REPO $SOL_TAG --squash - fi -fi - -read -p "Update rapidjson (y/n) " ok -if [[ $ok =~ [yY] ]] -then - if [ -d "third_party/rapidjson" ]; then - git subtree pull -P third_party/rapidjson/ $RAPIDJSON_REPO $RAPIDJSON_TAG --squash - else - git subtree add -P third_party/rapidjson/ $RAPIDJSON_REPO $RAPIDJSON_TAG --squash - fi -fi - -read -p "Update microtar (y/n) " ok -if [[ $ok =~ [yY] ]] -then - if [ -d "third_party/microtar" ]; then - git subtree pull -P third_party/microtar/ $MICROTAR_REPO $MICROTAR_TAG --squash - else - git subtree add -P third_party/microtar/ $MICROTAR_REPO $MICROTAR_TAG --squash - fi -fi +PROTOZERO_PATH="mapbox/protozero" +PROTOZERO_TAG=v1.6.2 + +VTZERO_PATH="mapbox/vtzero" +VTZERO_TAG=v1.0.1 + +function update_subtree () { + name=${1^^} + path=$(tmpvar=${name}_PATH && echo ${!tmpvar}) + tag=$(tmpvar=${name}_TAG && echo ${!tmpvar}) + dir=$(basename $path) + repo="https://github.com/${path}.git" + latest=$(curl -s "https://api.github.com/repos/${path}/releases/latest" | jq ".tag_name") + + echo "Latest $1 release is ${latest}, pulling in \"${tag}\"" + + read -p "Update ${1} (y/n) " ok + + if [[ $ok =~ [yY] ]] + then + if [ -d "third_party/$dir" ]; then + git subtree pull -P third_party/$dir ${repo} ${tag} --squash + else + git subtree add -P third_party/$dir ${repo} ${tag} --squash + fi + fi +} + +## Update dependencies +for dep in osmium variant sol rapidjson microtar protozero vtzero ; do + update_subtree $dep +done diff --git a/src/contractor/contractor.cpp b/src/contractor/contractor.cpp index 416f4a62b06..266367080aa 100644 --- a/src/contractor/contractor.cpp +++ b/src/contractor/contractor.cpp @@ -75,6 +75,12 @@ int Contractor::Run() EdgeID number_of_edge_based_nodes = updater.LoadAndUpdateEdgeExpandedGraph( edge_based_edge_list, node_weights, connectivity_checksum); + // Convert node weights for oneway streets to INVALID_EDGE_WEIGHT + for (auto &weight : node_weights) + { + weight = (weight & 0x80000000) ? INVALID_EDGE_WEIGHT : weight; + } + // Contracting the edge-expanded graph TIMER_START(contraction); diff --git a/src/customize/customizer.cpp b/src/customize/customizer.cpp index 50007e4f4f4..2c4f57ab893 100644 --- a/src/customize/customizer.cpp +++ b/src/customize/customizer.cpp @@ -74,24 +74,28 @@ void printUnreachableStatistics(const Partition &partition, auto LoadAndUpdateEdgeExpandedGraph(const CustomizationConfig &config, const partitioner::MultiLevelPartition &mlp, + std::vector &node_weights, + std::vector &node_durations, std::uint32_t &connectivity_checksum) { updater::Updater updater(config.updater_config); - EdgeID num_nodes; std::vector edge_based_edge_list; - std::tie(num_nodes, edge_based_edge_list, connectivity_checksum) = - updater.LoadAndUpdateEdgeExpandedGraph(); + EdgeID num_nodes = updater.LoadAndUpdateEdgeExpandedGraph( + edge_based_edge_list, node_weights, node_durations, connectivity_checksum); auto directed = partitioner::splitBidirectionalEdges(edge_based_edge_list); - auto tidied = - partitioner::prepareEdgesForUsageInGraph(std::move(directed)); - auto edge_based_graph = customizer::MultiLevelEdgeBasedGraph(mlp, num_nodes, std::move(tidied)); + + auto tidied = partitioner::prepareEdgesForUsageInGraph< + typename partitioner::MultiLevelEdgeBasedGraph::InputEdge>(std::move(directed)); + + auto edge_based_graph = + partitioner::MultiLevelEdgeBasedGraph(mlp, num_nodes, std::move(tidied)); return edge_based_graph; } -std::vector customizeFilteredMetrics(const MultiLevelEdgeBasedGraph &graph, +std::vector customizeFilteredMetrics(const partitioner::MultiLevelEdgeBasedGraph &graph, const partitioner::CellStorage &storage, const CellCustomizer &customizer, const std::vector> &node_filters) @@ -119,8 +123,13 @@ int Customizer::Run(const CustomizationConfig &config) partitioner::MultiLevelPartition mlp; partitioner::files::readPartition(config.GetPath(".osrm.partition"), mlp); + std::vector node_weights; + std::vector node_durations; // TODO: to be removed later std::uint32_t connectivity_checksum = 0; - auto graph = LoadAndUpdateEdgeExpandedGraph(config, mlp, connectivity_checksum); + auto graph = LoadAndUpdateEdgeExpandedGraph( + config, mlp, node_weights, node_durations, connectivity_checksum); + BOOST_ASSERT(graph.GetNumberOfNodes() == node_weights.size()); + std::for_each(node_weights.begin(), node_weights.end(), [](auto &w) { w &= 0x7fffffff; }); util::Log() << "Loaded edge based graph: " << graph.GetNumberOfEdges() << " edges, " << graph.GetNumberOfNodes() << " nodes"; @@ -157,7 +166,10 @@ int Customizer::Run(const CustomizationConfig &config) util::Log() << "MLD customization writing took " << TIMER_SEC(writing_mld_data) << " seconds"; TIMER_START(writing_graph); - partitioner::files::writeGraph(config.GetPath(".osrm.mldgr"), graph, connectivity_checksum); + MultiLevelEdgeBasedGraph shaved_graph{ + std::move(graph), std::move(node_weights), std::move(node_durations)}; + customizer::files::writeGraph( + config.GetPath(".osrm.mldgr"), shaved_graph, connectivity_checksum); TIMER_STOP(writing_graph); util::Log() << "Graph writing took " << TIMER_SEC(writing_graph) << " seconds"; diff --git a/src/engine/plugins/table.cpp b/src/engine/plugins/table.cpp index 54804788eb4..e10b5461ace 100644 --- a/src/engine/plugins/table.cpp +++ b/src/engine/plugins/table.cpp @@ -81,16 +81,21 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, } auto snapped_phantoms = SnapPhantomNodes(phantom_nodes); - auto result_table = - algorithms.ManyToManySearch(snapped_phantoms, params.sources, params.destinations); - if (result_table.empty()) + bool request_distance = params.annotations & api::TableParameters::AnnotationsType::Distance; + bool request_duration = params.annotations & api::TableParameters::AnnotationsType::Duration; + + auto result_tables_pair = algorithms.ManyToManySearch( + snapped_phantoms, params.sources, params.destinations, request_distance, request_duration); + + if ((request_duration && result_tables_pair.first.empty()) || + (request_distance && result_tables_pair.second.empty())) { return Error("NoTable", "No table found", result); } api::TableAPI table_api{facade, params}; - table_api.MakeResponse(result_table, snapped_phantoms, result); + table_api.MakeResponse(result_tables_pair, snapped_phantoms, result); return Status::Ok; } diff --git a/src/engine/plugins/tile.cpp b/src/engine/plugins/tile.cpp index 46a1493dd4b..bd776756640 100644 --- a/src/engine/plugins/tile.cpp +++ b/src/engine/plugins/tile.cpp @@ -1,5 +1,6 @@ #include "guidance/turn_instruction.hpp" +#include "engine/plugins/plugin_base.hpp" #include "engine/plugins/plugin_base.hpp" #include "engine/plugins/tile.hpp" @@ -8,21 +9,19 @@ #include "util/vector_tile.hpp" #include "util/web_mercator.hpp" -#include "engine/api/json_factory.hpp" - #include #include #include #include -#include -#include +#include +#include +#include #include #include #include #include -#include #include #include @@ -41,45 +40,7 @@ constexpr const static int MIN_ZOOM_FOR_TURNS = 15; namespace { -// Creates an indexed lookup table for values - used to encoded the vector tile -// which uses a lookup table and index pointers for encoding -template struct ValueIndexer -{ - private: - std::vector used_values; - std::unordered_map value_offsets; - - public: - std::size_t add(const T &value) - { - const auto found = value_offsets.find(value); - std::size_t offset; - - if (found == value_offsets.end()) - { - used_values.push_back(value); - offset = used_values.size() - 1; - value_offsets[value] = offset; - } - else - { - offset = found->second; - } - - return offset; - } - - std::size_t indexOf(const T &value) { return value_offsets[value]; } - - const std::vector &values() { return used_values; } - - std::size_t size() const { return used_values.size(); } -}; - using RTreeLeaf = datafacade::BaseDataFacade::RTreeLeaf; -// TODO: Port all this encoding logic to https://github.com/mapbox/vector-tile, which wasn't -// available when this code was originally written. - // Simple container class for WGS84 coordinates template struct Point final { @@ -132,58 +93,15 @@ const static box_t clip_box(point_t(-util::vector_tile::BUFFER, -util::vector_ti point_t(util::vector_tile::EXTENT + util::vector_tile::BUFFER, util::vector_tile::EXTENT + util::vector_tile::BUFFER)); -// from mapnik-vector-tile -// Encodes a linestring using protobuf zigzag encoding -inline bool encodeLinestring(const FixedLine &line, - protozero::packed_field_uint32 &geometry, - std::int32_t &start_x, - std::int32_t &start_y) -{ - const std::size_t line_size = line.size(); - if (line_size < 2) - { - return false; - } - - const unsigned lineto_count = static_cast(line_size) - 1; - - auto pt = line.begin(); - const constexpr int MOVETO_COMMAND = 9; - geometry.add_element(MOVETO_COMMAND); // move_to | (1 << 3) - geometry.add_element(protozero::encode_zigzag32(pt->x - start_x)); - geometry.add_element(protozero::encode_zigzag32(pt->y - start_y)); - start_x = pt->x; - start_y = pt->y; - // This means LINETO repeated N times - // See: https://github.com/mapbox/vector-tile-spec/tree/master/2.1#example-command-integers - geometry.add_element((lineto_count << 3u) | 2u); - // Now that we've issued the LINETO REPEAT N command, we append - // N coordinate pairs immediately after the command. - for (++pt; pt != line.end(); ++pt) - { - const std::int32_t dx = pt->x - start_x; - const std::int32_t dy = pt->y - start_y; - geometry.add_element(protozero::encode_zigzag32(dx)); - geometry.add_element(protozero::encode_zigzag32(dy)); - start_x = pt->x; - start_y = pt->y; - } - return true; -} - -// from mapnik-vctor-tile -// Encodes a point -inline void encodePoint(const FixedPoint &pt, protozero::packed_field_uint32 &geometry) -{ - const constexpr int MOVETO_COMMAND = 9; - geometry.add_element(MOVETO_COMMAND); - const std::int32_t dx = pt.x; - const std::int32_t dy = pt.y; - // Manual zigzag encoding. - geometry.add_element(protozero::encode_zigzag32(dx)); - geometry.add_element(protozero::encode_zigzag32(dy)); -} - +/** + * Return the x1,y1,x2,y2 pixel coordinates of a line in a given + * tile. + * + * @param start the first coordinate of the line + * @param target the last coordinate of the line + * @param tile_bbox the boundaries of the tile, in mercator coordinates + * @return a FixedLine with coordinates relative to the tile_bbox. + */ linestring_t floatLineToTileLine(const FloatLine &geo_line, const BBox &tile_bbox) { linestring_t unclipped_line; @@ -361,6 +279,144 @@ std::vector getSegregatedNodes(const DataFacadeBase &facade, return result; } +struct SpeedLayer : public vtzero::layer_builder +{ + + vtzero::value_index_small_uint uint_index; + vtzero::value_index double_index; + vtzero::value_index_internal string_index; + vtzero::value_index_bool bool_index; + + vtzero::index_value key_speed; + vtzero::index_value key_is_small; + vtzero::index_value key_datasource; + vtzero::index_value key_weight; + vtzero::index_value key_duration; + vtzero::index_value key_name; + vtzero::index_value key_rate; + + SpeedLayer(vtzero::tile_builder &tile) + : layer_builder(tile, "speeds"), uint_index(*this), double_index(*this), + string_index(*this), bool_index(*this), key_speed(add_key_without_dup_check("speed")), + key_is_small(add_key_without_dup_check("is_small")), + key_datasource(add_key_without_dup_check("datasource")), + key_weight(add_key_without_dup_check("weight")), + key_duration(add_key_without_dup_check("duration")), + key_name(add_key_without_dup_check("name")), key_rate(add_key_without_dup_check("rate")) + { + } + +}; // struct SpeedLayer + +class SpeedLayerFeatureBuilder : public vtzero::linestring_feature_builder +{ + + SpeedLayer &m_layer; + + public: + SpeedLayerFeatureBuilder(SpeedLayer &layer, uint64_t id) + : vtzero::linestring_feature_builder(layer), m_layer(layer) + { + set_id(id); + } + + void set_speed(unsigned int value) + { + add_property(m_layer.key_speed, m_layer.uint_index(std::min(value, 127u))); + } + + void set_is_small(bool value) { add_property(m_layer.key_is_small, m_layer.bool_index(value)); } + + void set_datasource(const std::string &value) + { + add_property(m_layer.key_datasource, + m_layer.string_index(vtzero::encoded_property_value{value})); + } + + void set_weight(double value) { add_property(m_layer.key_weight, m_layer.double_index(value)); } + + void set_duration(double value) + { + add_property(m_layer.key_duration, m_layer.double_index(value)); + } + + void set_name(const boost::string_ref &value) + { + add_property( + m_layer.key_name, + m_layer.string_index(vtzero::encoded_property_value{value.data(), value.size()})); + } + + void set_rate(double value) { add_property(m_layer.key_rate, m_layer.double_index(value)); } + +}; // class SpeedLayerFeatureBuilder + +struct TurnsLayer : public vtzero::layer_builder +{ + + vtzero::value_index int_index; + vtzero::value_index float_index; + vtzero::value_index_internal string_index; + + vtzero::index_value key_bearing_in; + vtzero::index_value key_turn_angle; + vtzero::index_value key_cost; + vtzero::index_value key_weight; + vtzero::index_value key_turn_type; + vtzero::index_value key_turn_modifier; + + TurnsLayer(vtzero::tile_builder &tile) + : layer_builder(tile, "turns"), int_index(*this), float_index(*this), string_index(*this), + key_bearing_in(add_key_without_dup_check("bearing_in")), + key_turn_angle(add_key_without_dup_check("turn_angle")), + key_cost(add_key_without_dup_check("cost")), + key_weight(add_key_without_dup_check("weight")), + key_turn_type(add_key_without_dup_check("type")), + key_turn_modifier(add_key_without_dup_check("modifier")) + { + } + +}; // struct TurnsLayer + +class TurnsLayerFeatureBuilder : public vtzero::point_feature_builder +{ + + TurnsLayer &m_layer; + + public: + TurnsLayerFeatureBuilder(TurnsLayer &layer, uint64_t id) + : vtzero::point_feature_builder(layer), m_layer(layer) + { + set_id(id); + } + + void set_bearing_in(int value) + { + add_property(m_layer.key_bearing_in, m_layer.int_index(value)); + } + + void set_turn_angle(int value) + { + add_property(m_layer.key_turn_angle, m_layer.int_index(value)); + } + + void set_cost(float value) { add_property(m_layer.key_cost, m_layer.float_index(value)); } + + void set_weight(float value) { add_property(m_layer.key_weight, m_layer.float_index(value)); } + + void set_turn(osrm::guidance::TurnInstruction value) + { + const auto type = osrm::guidance::internalInstructionTypeToString(value.type); + const auto modifier = osrm::guidance::instructionModifierToString(value.direction_modifier); + add_property( + m_layer.key_turn_type, + m_layer.string_index(vtzero::encoded_property_value{type.data(), type.size()})); + add_property( + m_layer.key_turn_modifier, + m_layer.string_index(vtzero::encoded_property_value{modifier.data(), modifier.size()})); + } +}; // class TurnsLayerFeatureBuilder + void encodeVectorTile(const DataFacadeBase &facade, unsigned x, unsigned y, @@ -371,117 +427,25 @@ void encodeVectorTile(const DataFacadeBase &facade, const std::vector &segregated_nodes, std::string &pbf_buffer) { - - std::uint8_t max_datasource_id = 0; - - // Vector tiles encode properties on features as indexes into a layer-specific - // lookup table. These ValueIndexer's act as memoizers for values as we discover - // them during edge explioration, and are then used to generate the lookup - // tables for each tile layer. - ValueIndexer line_int_index; - ValueIndexer line_string_index; - ValueIndexer point_int_index; - ValueIndexer point_float_index; - ValueIndexer point_string_index; + vtzero::tile_builder tile; const auto get_geometry_id = [&facade](auto edge) { return facade.GetGeometryIndex(edge.forward_segment_id.id).id; }; - // Vector tiles encode feature properties as indexes into a lookup table. So, we need - // to "pre-loop" over all the edges to create the lookup tables. Once we have those, we - // can then encode the features, and we'll know the indexes that feature properties - // need to refer to. - for (const auto &edge_index : sorted_edge_indexes) - { - const auto &edge = edges[edge_index]; - - const auto geometry_id = get_geometry_id(edge); - const auto forward_datasource_range = facade.GetUncompressedForwardDatasources(geometry_id); - const auto reverse_datasource_range = facade.GetUncompressedReverseDatasources(geometry_id); - - BOOST_ASSERT(edge.fwd_segment_position < forward_datasource_range.size()); - const auto forward_datasource = forward_datasource_range(edge.fwd_segment_position); - BOOST_ASSERT(edge.fwd_segment_position < reverse_datasource_range.size()); - const auto reverse_datasource = reverse_datasource_range(reverse_datasource_range.size() - - edge.fwd_segment_position - 1); - - // Keep track of the highest datasource seen so that we don't write unnecessary - // data to the layer attribute values - max_datasource_id = std::max(max_datasource_id, forward_datasource); - max_datasource_id = std::max(max_datasource_id, reverse_datasource); - } - // Convert tile coordinates into mercator coordinates double min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat; util::web_mercator::xyzToMercator( x, y, z, min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat); const BBox tile_bbox{min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat}; - // Protobuf serializes blocks when objects go out of scope, hence - // the extra scoping below. - protozero::pbf_writer tile_writer{pbf_buffer}; + // XXX leaving in some superfluous scopes to make diff easier to read. { { - // Add a layer object to the PBF stream. 3=='layer' from the vector tile spec - // (2.1) - protozero::pbf_writer line_layer_writer(tile_writer, util::vector_tile::LAYER_TAG); - // TODO: don't write a layer if there are no features - - line_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version - // Field 1 is the "layer name" field, it's a string - line_layer_writer.add_string(util::vector_tile::NAME_TAG, "speeds"); // name - // Field 5 is the tile extent. It's a uint32 and should be set to 4096 - // for normal vector tiles. - line_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG, - util::vector_tile::EXTENT); // extent - - // Because we need to know the indexes into the vector tile lookup table, - // we need to do an initial pass over the data and create the complete - // index of used values. - for (const auto &edge_index : sorted_edge_indexes) - { - const auto &edge = edges[edge_index]; - const auto geometry_id = get_geometry_id(edge); - - // Get coordinates for start/end nodes of segment (NodeIDs u and v) - const auto a = facade.GetCoordinateOfNode(edge.u); - const auto b = facade.GetCoordinateOfNode(edge.v); - // Calculate the length in meters - const double length = osrm::util::coordinate_calculation::haversineDistance(a, b); - - // Weight values - const auto forward_weight_range = facade.GetUncompressedForwardWeights(geometry_id); - const auto reverse_weight_range = facade.GetUncompressedReverseWeights(geometry_id); - const auto forward_weight = forward_weight_range[edge.fwd_segment_position]; - const auto reverse_weight = reverse_weight_range[reverse_weight_range.size() - - edge.fwd_segment_position - 1]; - line_int_index.add(forward_weight); - line_int_index.add(reverse_weight); - - std::uint32_t forward_rate = - static_cast(round(length / forward_weight * 10.)); - std::uint32_t reverse_rate = - static_cast(round(length / reverse_weight * 10.)); - - line_int_index.add(forward_rate); - line_int_index.add(reverse_rate); - - // Duration values - const auto forward_duration_range = - facade.GetUncompressedForwardDurations(geometry_id); - const auto reverse_duration_range = - facade.GetUncompressedReverseDurations(geometry_id); - const auto forward_duration = forward_duration_range[edge.fwd_segment_position]; - const auto reverse_duration = reverse_duration_range[reverse_duration_range.size() - - edge.fwd_segment_position - 1]; - - line_int_index.add(forward_duration); - line_int_index.add(reverse_duration); - } - // Begin the layer features block { + SpeedLayer speeds_layer{tile}; + // Each feature gets a unique id, starting at 1 unsigned id = 1; for (const auto &edge_index : sorted_edge_indexes) @@ -516,7 +480,6 @@ void encodeVectorTile(const DataFacadeBase &facade, const auto reverse_duration = reverse_duration_range[reverse_duration_range.size() - edge.fwd_segment_position - 1]; - const auto forward_datasource_idx = forward_datasource_range(edge.fwd_segment_position); const auto reverse_datasource_idx = reverse_datasource_range( @@ -526,84 +489,10 @@ void encodeVectorTile(const DataFacadeBase &facade, const auto name_id = facade.GetNameIndex(edge.forward_segment_id.id); auto name = facade.GetNameForID(name_id); - line_string_index.add(name); - - const auto encode_tile_line = [&line_layer_writer, - &component_id, - &id, - &max_datasource_id, - &line_int_index]( - const FixedLine &tile_line, - const std::uint32_t speed_kmh_idx, - const std::uint32_t rate_idx, - const std::size_t weight_idx, - const std::size_t duration_idx, - const DatasourceID datasource_idx, - const std::size_t name_idx, - std::int32_t &start_x, - std::int32_t &start_y) { - // Here, we save the two attributes for our feature: the speed and - // the is_small boolean. We only serve up speeds from 0-139, so all we - // do is save the first - protozero::pbf_writer feature_writer(line_layer_writer, - util::vector_tile::FEATURE_TAG); - // Field 3 is the "geometry type" field. Value 2 is "line" - feature_writer.add_enum( - util::vector_tile::GEOMETRY_TAG, - util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type - // Field 1 for the feature is the "id" field. - feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id - { - // When adding attributes to a feature, we have to write - // pairs of numbers. The first value is the index in the - // keys array (written later), and the second value is the - // index into the "values" array (also written later). We're - // not writing the actual speed or bool value here, we're saving - // an index into the "values" array. This means many features - // can share the same value data, leading to smaller tiles. - protozero::packed_field_uint32 field( - feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG); - - field.add_element(0); // "speed" tag key offset - field.add_element(std::min( - speed_kmh_idx, 127u)); // save the speed value, capped at 127 - field.add_element(1); // "is_small" tag key offset - field.add_element( - 128 + (component_id.is_tiny ? 0 : 1)); // is_small feature offset - field.add_element(2); // "datasource" tag key offset - field.add_element(130 + datasource_idx); // datasource value offset - field.add_element(3); // "weight" tag key offset - field.add_element(130 + max_datasource_id + 1 + - weight_idx); // weight value offset - field.add_element(4); // "duration" tag key offset - field.add_element(130 + max_datasource_id + 1 + - duration_idx); // duration value offset - field.add_element(5); // "name" tag key offset - - field.add_element(130 + max_datasource_id + 1 + - line_int_index.values().size() + name_idx); - - field.add_element(6); // rate tag key offset - field.add_element(130 + max_datasource_id + 1 + rate_idx); - } - { - - // Encode the geometry for the feature - protozero::packed_field_uint32 geometry( - feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); - encodeLinestring(tile_line, geometry, start_x, start_y); - } - }; - // If this is a valid forward edge, go ahead and add it to the tile if (forward_duration != 0 && edge.forward_segment_id.enabled) { - std::int32_t start_x = 0; - std::int32_t start_y = 0; - // Calculate the speed for this line - // Speeds are looked up in a simple 1:1 table, so the speed value == lookup - // table index std::uint32_t speed_kmh_idx = static_cast(round(length / forward_duration * 10 * 3.6)); @@ -616,15 +505,19 @@ void encodeVectorTile(const DataFacadeBase &facade, auto tile_line = coordinatesToTileLine(a, b, tile_bbox); if (!tile_line.empty()) { - encode_tile_line(tile_line, - speed_kmh_idx, - line_int_index.indexOf(forward_rate), - line_int_index.indexOf(forward_weight), - line_int_index.indexOf(forward_duration), - forward_datasource_idx, - line_string_index.indexOf(name), - start_x, - start_y); + SpeedLayerFeatureBuilder fbuilder{speeds_layer, id}; + fbuilder.add_linestring_from_container(tile_line); + + fbuilder.set_speed(speed_kmh_idx); + fbuilder.set_is_small(component_id.is_tiny); + fbuilder.set_datasource( + facade.GetDatasourceName(forward_datasource_idx).to_string()); + fbuilder.set_weight(forward_weight / 10.0); + fbuilder.set_duration(forward_duration / 10.0); + fbuilder.set_name(name); + fbuilder.set_rate(forward_rate / 10.0); + + fbuilder.commit(); } } @@ -632,12 +525,7 @@ void encodeVectorTile(const DataFacadeBase &facade, // properties if (reverse_duration != 0 && edge.reverse_segment_id.enabled) { - std::int32_t start_x = 0; - std::int32_t start_y = 0; - // Calculate the speed for this line - // Speeds are looked up in a simple 1:1 table, so the speed value == lookup - // table index std::uint32_t speed_kmh_idx = static_cast(round(length / reverse_duration * 10 * 3.6)); @@ -650,232 +538,52 @@ void encodeVectorTile(const DataFacadeBase &facade, auto tile_line = coordinatesToTileLine(b, a, tile_bbox); if (!tile_line.empty()) { - encode_tile_line(tile_line, - speed_kmh_idx, - line_int_index.indexOf(reverse_rate), - line_int_index.indexOf(reverse_weight), - line_int_index.indexOf(reverse_duration), - reverse_datasource_idx, - line_string_index.indexOf(name), - start_x, - start_y); + SpeedLayerFeatureBuilder fbuilder{speeds_layer, id}; + fbuilder.add_linestring_from_container(tile_line); + + fbuilder.set_speed(speed_kmh_idx); + fbuilder.set_is_small(component_id.is_tiny); + fbuilder.set_datasource( + facade.GetDatasourceName(reverse_datasource_idx).to_string()); + fbuilder.set_weight(reverse_weight / 10.0); + fbuilder.set_duration(reverse_duration / 10.0); + fbuilder.set_name(name); + fbuilder.set_rate(reverse_rate / 10.0); + + fbuilder.commit(); } } } } - - // Field id 3 is the "keys" attribute - // We need two "key" fields, these are referred to with 0 and 1 (their array - // indexes) earlier - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "speed"); - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "is_small"); - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "datasource"); - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight"); - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "duration"); - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "name"); - line_layer_writer.add_string(util::vector_tile::KEY_TAG, "rate"); - - // Now, we write out the possible speed value arrays and possible is_tiny - // values. Field type 4 is the "values" field. It's a variable type field, - // so requires a two-step write (create the field, then write its value) - for (std::size_t i = 0; i < 128; i++) - { - // Writing field type 4 == variant type - protozero::pbf_writer values_writer(line_layer_writer, - util::vector_tile::VARIANT_TAG); - // Attribute value 5 == uint64 type - values_writer.add_uint64(util::vector_tile::VARIANT_TYPE_UINT64, i); - } - { - protozero::pbf_writer values_writer(line_layer_writer, - util::vector_tile::VARIANT_TAG); - // Attribute value 7 == bool type - values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, true); - } - { - protozero::pbf_writer values_writer(line_layer_writer, - util::vector_tile::VARIANT_TAG); - // Attribute value 7 == bool type - values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, false); - } - for (std::size_t i = 0; i <= max_datasource_id; i++) - { - // Writing field type 4 == variant type - protozero::pbf_writer values_writer(line_layer_writer, - util::vector_tile::VARIANT_TAG); - // Attribute value 1 == string type - values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, - facade.GetDatasourceName(i).to_string()); - } - for (auto value : line_int_index.values()) - { - // Writing field type 4 == variant type - protozero::pbf_writer values_writer(line_layer_writer, - util::vector_tile::VARIANT_TAG); - // Attribute value 2 == float type - // Durations come out of OSRM in integer deciseconds, so we convert them - // to seconds with a simple /10 for display - values_writer.add_double(util::vector_tile::VARIANT_TYPE_DOUBLE, value / 10.); - } - - for (const auto &name : line_string_index.values()) - { - // Writing field type 4 == variant type - protozero::pbf_writer values_writer(line_layer_writer, - util::vector_tile::VARIANT_TAG); - // Attribute value 1 == string type - values_writer.add_string( - util::vector_tile::VARIANT_TYPE_STRING, name.data(), name.size()); - } } // Only add the turn layer to the tile if it has some features (we sometimes won't // for tiles of z<16, and tiles that don't show any intersections) if (!all_turn_data.empty()) { - - struct EncodedTurnData - { - util::Coordinate coordinate; - std::size_t angle_index; - std::size_t turn_index; - std::size_t duration_index; - std::size_t weight_index; - std::size_t turntype_index; - std::size_t turnmodifier_index; - }; - // we need to pre-encode all values here because we need the full offsets later - // for encoding the actual features. - std::vector encoded_turn_data(all_turn_data.size()); - std::transform( - all_turn_data.begin(), - all_turn_data.end(), - encoded_turn_data.begin(), - [&](const routing_algorithms::TurnData &t) { - auto angle_idx = point_int_index.add(t.in_angle); - auto turn_idx = point_int_index.add(t.turn_angle); - auto duration_idx = - point_float_index.add(t.duration / 10.0); // Note conversion to float here - auto weight_idx = - point_float_index.add(t.weight / 10.0); // Note conversion to float here - - auto turntype_idx = point_string_index.add( - osrm::guidance::internalInstructionTypeToString(t.turn_instruction.type)); - auto turnmodifier_idx = - point_string_index.add(osrm::guidance::instructionModifierToString( - t.turn_instruction.direction_modifier)); - return EncodedTurnData{t.coordinate, - angle_idx, - turn_idx, - duration_idx, - weight_idx, - turntype_idx, - turnmodifier_idx}; - }); - - // Now write the points layer for turn penalty data: - // Add a layer object to the PBF stream. 3=='layer' from the vector tile spec - // (2.1) - protozero::pbf_writer point_layer_writer(tile_writer, util::vector_tile::LAYER_TAG); - point_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version - point_layer_writer.add_string(util::vector_tile::NAME_TAG, "turns"); // name - point_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG, - util::vector_tile::EXTENT); // extent - - // Begin writing the set of point features + TurnsLayer turns_layer{tile}; + uint64_t id = 0; + for (const auto &turn_data : all_turn_data) { - // Start each features with an ID starting at 1 - int id = 1; - - // Helper function to encode a new point feature on a vector tile. - const auto encode_tile_point = [&](const FixedPoint &tile_point, - const auto &point_turn_data) { - protozero::pbf_writer feature_writer(point_layer_writer, - util::vector_tile::FEATURE_TAG); - // Field 3 is the "geometry type" field. Value 1 is "point" - feature_writer.add_enum( - util::vector_tile::GEOMETRY_TAG, - util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type - feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id - { - // Write out the 4 properties we want on the feature. These - // refer to indexes in the properties lookup table, which we - // add to the tile after we add all features. - protozero::packed_field_uint32 field( - feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG); - field.add_element(0); // "bearing_in" tag key offset - field.add_element(point_turn_data.angle_index); - field.add_element(1); // "turn_angle" tag key offset - field.add_element(point_turn_data.turn_index); - field.add_element(2); // "cost" tag key offset - field.add_element(point_int_index.size() + point_turn_data.duration_index); - field.add_element(3); // "weight" tag key offset - field.add_element(point_int_index.size() + point_turn_data.weight_index); - field.add_element(4); // "type" tag key offset - field.add_element(point_int_index.size() + point_float_index.size() + - point_turn_data.turntype_index); - field.add_element(5); // "modifier" tag key offset - field.add_element(point_int_index.size() + point_float_index.size() + - point_turn_data.turnmodifier_index); - } - { - // Add the geometry as the last field in this feature - protozero::packed_field_uint32 geometry( - feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); - encodePoint(tile_point, geometry); - } - }; - - // Loop over all the turns we found and add them as features to the layer - for (const auto &turndata : encoded_turn_data) + const auto tile_point = coordinatesToTilePoint(turn_data.coordinate, tile_bbox); + if (boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box)) { - const auto tile_point = coordinatesToTilePoint(turndata.coordinate, tile_bbox); - if (!boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box)) - { - continue; - } - encode_tile_point(tile_point, turndata); - } - } + TurnsLayerFeatureBuilder fbuilder{turns_layer, ++id}; + fbuilder.add_point(tile_point); - // Add the names of the three attributes we added to all the turn penalty - // features previously. The indexes used there refer to these keys. - point_layer_writer.add_string(util::vector_tile::KEY_TAG, "bearing_in"); - point_layer_writer.add_string(util::vector_tile::KEY_TAG, "turn_angle"); - point_layer_writer.add_string(util::vector_tile::KEY_TAG, "cost"); - point_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight"); - point_layer_writer.add_string(util::vector_tile::KEY_TAG, "type"); - point_layer_writer.add_string(util::vector_tile::KEY_TAG, "modifier"); - - // Now, save the lists of integers and floats that our features refer to. - for (const auto &value : point_int_index.values()) - { - protozero::pbf_writer values_writer(point_layer_writer, - util::vector_tile::VARIANT_TAG); - values_writer.add_sint64(util::vector_tile::VARIANT_TYPE_SINT64, value); - } - for (const auto &value : point_float_index.values()) - { - protozero::pbf_writer values_writer(point_layer_writer, - util::vector_tile::VARIANT_TAG); - values_writer.add_float(util::vector_tile::VARIANT_TYPE_FLOAT, value); - } - for (const auto &value : point_string_index.values()) - { - protozero::pbf_writer values_writer(point_layer_writer, - util::vector_tile::VARIANT_TAG); - values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, value); + fbuilder.set_bearing_in(turn_data.in_angle); + fbuilder.set_turn_angle(turn_data.turn_angle); + fbuilder.set_cost(turn_data.duration / 10.0); + fbuilder.set_weight(turn_data.weight / 10.0); + fbuilder.set_turn(turn_data.turn_instruction); + + fbuilder.commit(); + } } } // OSM Node tile layer { - protozero::pbf_writer point_layer_writer(tile_writer, util::vector_tile::LAYER_TAG); - point_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version - point_layer_writer.add_string(util::vector_tile::NAME_TAG, "osmnodes"); // name - point_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG, - util::vector_tile::EXTENT); // extent - std::vector internal_nodes; internal_nodes.reserve(edges.size() * 2); for (const auto &edge : edges) @@ -887,6 +595,8 @@ void encodeVectorTile(const DataFacadeBase &facade, auto new_end = std::unique(internal_nodes.begin(), internal_nodes.end()); internal_nodes.resize(new_end - internal_nodes.begin()); + vtzero::layer_builder osmnodes_layer{tile, "osmnodes"}; + for (const auto &internal_node : internal_nodes) { const auto coord = facade.GetCoordinateOfNode(internal_node); @@ -895,32 +605,19 @@ void encodeVectorTile(const DataFacadeBase &facade, { continue; } - protozero::pbf_writer feature_writer(point_layer_writer, - util::vector_tile::FEATURE_TAG); - // Field 3 is the "geometry type" field. Value 1 is "point" - feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG, - util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type - const auto osmid = - static_cast(facade.GetOSMNodeIDOfNode(internal_node)); - feature_writer.add_uint64(util::vector_tile::ID_TAG, osmid); // id - // There are no additional properties, just the ID and the geometry - { - // Add the geometry as the last field in this feature - protozero::packed_field_uint32 geometry( - feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); - encodePoint(tile_point, geometry); - } + + vtzero::point_feature_builder fbuilder{osmnodes_layer}; + fbuilder.set_id( + static_cast(facade.GetOSMNodeIDOfNode(internal_node))); + fbuilder.add_point(tile_point); + fbuilder.commit(); } } + // Internal nodes tile layer { - protozero::pbf_writer line_layer_writer(tile_writer, util::vector_tile::LAYER_TAG); - line_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version - line_layer_writer.add_string(util::vector_tile::NAME_TAG, "internal-nodes"); // name - line_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG, - util::vector_tile::EXTENT); // extent + vtzero::layer_builder internal_nodes_layer{tile, "internal-nodes"}; - unsigned id = 0; for (auto edgeNodeID : segregated_nodes) { auto const geomIndex = facade.GetGeometryIndex(edgeNodeID); @@ -937,46 +634,21 @@ void encodeVectorTile(const DataFacadeBase &facade, points.push_back(facade.GetCoordinateOfNode(nodeID)); } - const auto encode_tile_line = [&line_layer_writer, &id]( - const FixedLine &tile_line, std::int32_t &start_x, std::int32_t &start_y) { - - protozero::pbf_writer feature_writer(line_layer_writer, - util::vector_tile::FEATURE_TAG); - - feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG, - util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type - - feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id - { - - protozero::packed_field_uint32 field( - feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG); - } - { - - // Encode the geometry for the feature - protozero::packed_field_uint32 geometry( - feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); - encodeLinestring(tile_line, geometry, start_x, start_y); - } - }; - - std::int32_t start_x = 0; - std::int32_t start_y = 0; - auto tile_lines = coordinatesToTileLine(points, tile_bbox); if (!tile_lines.empty()) { - for (auto const &tl : tile_lines) + vtzero::linestring_feature_builder fbuilder{internal_nodes_layer}; + for (auto const &tile_line : tile_lines) { - encode_tile_line(tl, start_x, start_y); + fbuilder.add_linestring_from_container(tile_line); } + fbuilder.commit(); } } } } - // protozero serializes data during object destructors, so once the scope closes, - // our result buffer will have all the tile data encoded into it. + + tile.serialize(pbf_buffer); } } diff --git a/src/engine/plugins/trip.cpp b/src/engine/plugins/trip.cpp index 2347677ff43..8914c3e4834 100644 --- a/src/engine/plugins/trip.cpp +++ b/src/engine/plugins/trip.cpp @@ -131,7 +131,7 @@ void ManipulateTableForFSE(const std::size_t source_id, result_table.SetValue(destination_id, i, INVALID_EDGE_WEIGHT); } - // set destination->source to zero so rountrip treats source and + // set destination->source to zero so roundtrip treats source and // destination as one location result_table.SetValue(destination_id, source_id, 0); @@ -216,61 +216,66 @@ Status TripPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, BOOST_ASSERT(snapped_phantoms.size() == number_of_locations); // compute the duration table of all phantom nodes - auto result_table = util::DistTableWrapper( - algorithms.ManyToManySearch(snapped_phantoms, {}, {}), number_of_locations); - - if (result_table.size() == 0) + auto result_duration_table = util::DistTableWrapper( + algorithms + .ManyToManySearch( + snapped_phantoms, {}, {}, /*requestDistance*/ false, /*requestDuration*/ true) + .first, + number_of_locations); + + if (result_duration_table.size() == 0) { return Status::Error; } const constexpr std::size_t BF_MAX_FEASABLE = 10; - BOOST_ASSERT_MSG(result_table.size() == number_of_locations * number_of_locations, + BOOST_ASSERT_MSG(result_duration_table.size() == number_of_locations * number_of_locations, "Distance Table has wrong size"); - if (!IsStronglyConnectedComponent(result_table)) + if (!IsStronglyConnectedComponent(result_duration_table)) { return Error("NoTrips", "No trip visiting all destinations possible.", json_result); } if (fixed_start && fixed_end) { - ManipulateTableForFSE(source_id, destination_id, result_table); + ManipulateTableForFSE(source_id, destination_id, result_duration_table); } - std::vector trip; - trip.reserve(number_of_locations); + std::vector duration_trip; + duration_trip.reserve(number_of_locations); // get an optimized order in which the destinations should be visited if (number_of_locations < BF_MAX_FEASABLE) { - trip = trip::BruteForceTrip(number_of_locations, result_table); + duration_trip = trip::BruteForceTrip(number_of_locations, result_duration_table); } else { - trip = trip::FarthestInsertionTrip(number_of_locations, result_table); + duration_trip = trip::FarthestInsertionTrip(number_of_locations, result_duration_table); } // rotate result such that roundtrip starts at node with index 0 // thist first if covers scenarios: !fixed_end || fixed_start || (fixed_start && fixed_end) if (!fixed_end || fixed_start) { - auto desired_start_index = std::find(std::begin(trip), std::end(trip), 0); - BOOST_ASSERT(desired_start_index != std::end(trip)); - std::rotate(std::begin(trip), desired_start_index, std::end(trip)); + auto desired_start_index = std::find(std::begin(duration_trip), std::end(duration_trip), 0); + BOOST_ASSERT(desired_start_index != std::end(duration_trip)); + std::rotate(std::begin(duration_trip), desired_start_index, std::end(duration_trip)); } else if (fixed_end && !fixed_start && parameters.roundtrip) { - auto desired_start_index = std::find(std::begin(trip), std::end(trip), destination_id); - BOOST_ASSERT(desired_start_index != std::end(trip)); - std::rotate(std::begin(trip), desired_start_index, std::end(trip)); + auto desired_start_index = + std::find(std::begin(duration_trip), std::end(duration_trip), destination_id); + BOOST_ASSERT(desired_start_index != std::end(duration_trip)); + std::rotate(std::begin(duration_trip), desired_start_index, std::end(duration_trip)); } // get the route when visiting all destinations in optimized order InternalRouteResult route = - ComputeRoute(algorithms, snapped_phantoms, trip, parameters.roundtrip); + ComputeRoute(algorithms, snapped_phantoms, duration_trip, parameters.roundtrip); // get api response - const std::vector> trips = {trip}; + const std::vector> trips = {duration_trip}; const std::vector routes = {route}; api::TripAPI trip_api{facade, parameters}; trip_api.MakeResponse(trips, routes, snapped_phantoms, json_result); diff --git a/src/engine/routing_algorithms/alternative_path_mld.cpp b/src/engine/routing_algorithms/alternative_path_mld.cpp index 76de45971ec..7fbe37dabfc 100644 --- a/src/engine/routing_algorithms/alternative_path_mld.cpp +++ b/src/engine/routing_algorithms/alternative_path_mld.cpp @@ -470,10 +470,10 @@ RandIt filterUnpackedPathsBySharing(RandIt first, if (shortest_path.edges.empty()) return last; - std::unordered_set edges; - edges.reserve(size * shortest_path.edges.size() * (1.25)); + std::unordered_set nodes; + nodes.reserve(size * shortest_path.nodes.size() * (1.25)); - edges.insert(begin(shortest_path.edges), end(shortest_path.edges)); + nodes.insert(begin(shortest_path.nodes), end(shortest_path.nodes)); const auto over_sharing_limit = [&](auto &unpacked) { if (unpacked.edges.empty()) @@ -482,20 +482,20 @@ RandIt filterUnpackedPathsBySharing(RandIt first, } EdgeWeight total_duration = 0; - const auto add_if_seen = [&](const EdgeWeight duration, const EdgeID edge) { - auto edge_duration = facade.GetEdgeData(edge).duration; - total_duration += edge_duration; - if (edges.count(edge) > 0) + const auto add_if_seen = [&](const EdgeWeight duration, const NodeID node) { + auto node_duration = facade.GetNodeDuration(node); + total_duration += node_duration; + if (nodes.count(node) > 0) { - return duration + edge_duration; + return duration + node_duration; } return duration; }; - const auto shared_weight = - std::accumulate(begin(unpacked.edges), end(unpacked.edges), EdgeWeight{0}, add_if_seen); + const auto shared_duration = std::accumulate( + begin(unpacked.nodes), end(unpacked.nodes), EdgeDuration{0}, add_if_seen); - unpacked.sharing = shared_weight / static_cast(total_duration); + unpacked.sharing = shared_duration / static_cast(total_duration); BOOST_ASSERT(unpacked.sharing >= 0.); BOOST_ASSERT(unpacked.sharing <= 1.); @@ -505,7 +505,7 @@ RandIt filterUnpackedPathsBySharing(RandIt first, } else { - edges.insert(begin(unpacked.edges), end(unpacked.edges)); + nodes.insert(begin(unpacked.nodes), end(unpacked.nodes)); return false; } }; diff --git a/src/engine/routing_algorithms/direct_shortest_path.cpp b/src/engine/routing_algorithms/direct_shortest_path.cpp index 182bb7a9e9f..08ca3249e24 100644 --- a/src/engine/routing_algorithms/direct_shortest_path.cpp +++ b/src/engine/routing_algorithms/direct_shortest_path.cpp @@ -1,5 +1,4 @@ #include "engine/routing_algorithms/direct_shortest_path.hpp" - #include "engine/routing_algorithms/routing_base.hpp" #include "engine/routing_algorithms/routing_base_ch.hpp" #include "engine/routing_algorithms/routing_base_mld.hpp" diff --git a/src/engine/routing_algorithms/many_to_many_ch.cpp b/src/engine/routing_algorithms/many_to_many_ch.cpp index 807406281f8..8ee02b5ec92 100644 --- a/src/engine/routing_algorithms/many_to_many_ch.cpp +++ b/src/engine/routing_algorithms/many_to_many_ch.cpp @@ -60,8 +60,8 @@ void relaxOutgoingEdges(const DataFacade &facade, if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward) { const NodeID to = facade.GetTarget(edge); - const auto edge_weight = data.weight; + const auto edge_duration = data.duration; BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); @@ -85,12 +85,13 @@ void relaxOutgoingEdges(const DataFacade &facade, } void forwardRoutingStep(const DataFacade &facade, - const unsigned row_idx, - const unsigned number_of_targets, + const std::size_t row_index, + const std::size_t number_of_targets, typename SearchEngineData::ManyToManyQueryHeap &query_heap, const std::vector &search_space_with_buckets, std::vector &weights_table, std::vector &durations_table, + std::vector &middle_nodes_table, const PhantomNode &phantom_node) { const auto node = query_heap.DeleteMin(); @@ -105,12 +106,12 @@ void forwardRoutingStep(const DataFacade &facade, for (const auto ¤t_bucket : boost::make_iterator_range(bucket_list)) { // Get target id from bucket entry - const auto column_idx = current_bucket.column_index; + const auto column_index = current_bucket.column_index; const auto target_weight = current_bucket.weight; const auto target_duration = current_bucket.duration; - auto ¤t_weight = weights_table[row_idx * number_of_targets + column_idx]; - auto ¤t_duration = durations_table[row_idx * number_of_targets + column_idx]; + auto ¤t_weight = weights_table[row_index * number_of_targets + column_index]; + auto ¤t_duration = durations_table[row_index * number_of_targets + column_index]; // Check if new weight is better auto new_weight = source_weight + target_weight; @@ -122,12 +123,14 @@ void forwardRoutingStep(const DataFacade &facade, { current_weight = std::min(current_weight, new_weight); current_duration = std::min(current_duration, new_duration); + middle_nodes_table[row_index * number_of_targets + column_index] = node; } } else if (std::tie(new_weight, new_duration) < std::tie(current_weight, current_duration)) { current_weight = new_weight; current_duration = new_duration; + middle_nodes_table[row_index * number_of_targets + column_index] = node; } } @@ -136,7 +139,7 @@ void forwardRoutingStep(const DataFacade &facade, } void backwardRoutingStep(const DataFacade &facade, - const unsigned column_idx, + const unsigned column_index, typename SearchEngineData::ManyToManyQueryHeap &query_heap, std::vector &search_space_with_buckets, const PhantomNode &phantom_node) @@ -144,9 +147,11 @@ void backwardRoutingStep(const DataFacade &facade, const auto node = query_heap.DeleteMin(); const auto target_weight = query_heap.GetKey(node); const auto target_duration = query_heap.GetData(node).duration; + const auto parent = query_heap.GetData(node).parent; // Store settled nodes in search space bucket - search_space_with_buckets.emplace_back(node, column_idx, target_weight, target_duration); + search_space_with_buckets.emplace_back( + node, parent, column_index, target_weight, target_duration); relaxOutgoingEdges( facade, node, target_weight, target_duration, query_heap, phantom_node); @@ -154,26 +159,187 @@ void backwardRoutingStep(const DataFacade &facade, } // namespace ch +void retrievePackedPathFromSearchSpace(const NodeID middle_node_id, + const unsigned column_index, + const std::vector &search_space_with_buckets, + std::vector &packed_leg) +{ + auto bucket_list = std::equal_range(search_space_with_buckets.begin(), + search_space_with_buckets.end(), + middle_node_id, + NodeBucket::ColumnCompare(column_index)); + + NodeID current_node_id = middle_node_id; + + BOOST_ASSERT_MSG(std::distance(bucket_list.first, bucket_list.second) == 1, + "The pointers are not pointing to the same element."); + + while (bucket_list.first->parent_node != current_node_id && + bucket_list.first != search_space_with_buckets.end()) + { + current_node_id = bucket_list.first->parent_node; + + packed_leg.emplace_back(current_node_id); + + bucket_list = std::equal_range(search_space_with_buckets.begin(), + search_space_with_buckets.end(), + current_node_id, + NodeBucket::ColumnCompare(column_index)); + } +} + +void calculateDistances(typename SearchEngineData::ManyToManyQueryHeap &query_heap, + const DataFacade &facade, + const std::vector &phantom_nodes, + const std::vector &target_indices, + const std::size_t row_index, + const std::size_t source_index, + const PhantomNode &source_phantom, + const std::size_t number_of_targets, + const std::vector &search_space_with_buckets, + std::vector &distances_table, + const std::vector &middle_nodes_table) +{ + std::vector packed_leg; + + for (auto column_index : util::irange(0, number_of_targets)) + { + const auto target_index = target_indices[column_index]; + const auto &target_phantom = phantom_nodes[target_index]; + + if (source_index == target_index) + { + distances_table[row_index * number_of_targets + column_index] = 0.0; + continue; + } + + NodeID middle_node_id = middle_nodes_table[row_index * number_of_targets + column_index]; + + if (middle_node_id == SPECIAL_NODEID) // takes care of one-ways + { + distances_table[row_index * number_of_targets + column_index] = INVALID_EDGE_DISTANCE; + continue; + } + + // Step 1: Find path from source to middle node + ch::retrievePackedPathFromSingleManyToManyHeap(query_heap, middle_node_id, packed_leg); + std::reverse(packed_leg.begin(), packed_leg.end()); + + packed_leg.push_back(middle_node_id); + + // Step 2: Find path from middle to target node + retrievePackedPathFromSearchSpace( + middle_node_id, column_index, search_space_with_buckets, packed_leg); + + if (packed_leg.size() == 1 && (needsLoopForward(source_phantom, target_phantom) || + needsLoopBackwards(source_phantom, target_phantom))) + { + auto weight = ch::getLoopWeight(facade, packed_leg.front()); + if (weight != INVALID_EDGE_WEIGHT) + packed_leg.push_back(packed_leg.front()); + } + if (!packed_leg.empty()) + { + auto annotation = + ch::calculateEBGNodeAnnotations(facade, packed_leg.begin(), packed_leg.end()); + + distances_table[row_index * number_of_targets + column_index] = annotation; + + // check the direction of travel to figure out how to calculate the offset to/from + // the source/target + if (source_phantom.forward_segment_id.id == packed_leg.front()) + { + // ............ <-- calculateEGBAnnotation returns distance from 0 to 3 + // -->s <-- subtract offset to start at source + // ......... <-- want this distance as result + // entry 0---1---2---3--- <-- 3 is exit node + EdgeDistance offset = source_phantom.GetForwardDistance(); + distances_table[row_index * number_of_targets + column_index] -= offset; + } + else if (source_phantom.reverse_segment_id.id == packed_leg.front()) + { + // ............ <-- calculateEGBAnnotation returns distance from 0 to 3 + // s<------- <-- subtract offset to start at source + // ... <-- want this distance + // entry 0---1---2---3 <-- 3 is exit node + EdgeDistance offset = source_phantom.GetReverseDistance(); + distances_table[row_index * number_of_targets + column_index] -= offset; + } + if (target_phantom.forward_segment_id.id == packed_leg.back()) + { + // ............ <-- calculateEGBAnnotation returns distance from 0 to 3 + // ++>t <-- add offset to get to target + // ................ <-- want this distance as result + // entry 0---1---2---3--- <-- 3 is exit node + EdgeDistance offset = target_phantom.GetForwardDistance(); + distances_table[row_index * number_of_targets + column_index] += offset; + } + else if (target_phantom.reverse_segment_id.id == packed_leg.back()) + { + // ............ <-- calculateEGBAnnotation returns distance from 0 to 3 + // <++t <-- add offset to get from target + // ................ <-- want this distance as result + // entry 0---1---2---3--- <-- 3 is exit node + EdgeDistance offset = target_phantom.GetReverseDistance(); + distances_table[row_index * number_of_targets + column_index] += offset; + } + } + else + { + // there is no shortcut to unpack. source and target are on the same EBG Node. + // if the offset of the target is greater than the offset of the source, subtract it + if (target_phantom.GetForwardDistance() > source_phantom.GetForwardDistance()) + { + // --------->t <-- offsets + // ->s <-- subtract source offset from target offset + // ......... <-- want this distance as result + // entry 0---1---2---3--- <-- 3 is exit node + EdgeDistance offset = + target_phantom.GetForwardDistance() - source_phantom.GetForwardDistance(); + distances_table[row_index * number_of_targets + column_index] = offset; + } + else + { + // s<--- <-- offsets + // t<--------- <-- subtract source offset from target offset + // ...... <-- want this distance as result + // entry 0---1---2---3--- <-- 3 is exit node + EdgeDistance offset = + target_phantom.GetReverseDistance() - source_phantom.GetReverseDistance(); + distances_table[row_index * number_of_targets + column_index] = offset; + } + } + packed_leg.clear(); + } +} + template <> -std::vector manyToManySearch(SearchEngineData &engine_working_data, - const DataFacade &facade, - const std::vector &phantom_nodes, - const std::vector &source_indices, - const std::vector &target_indices) +std::pair, std::vector> +manyToManySearch(SearchEngineData &engine_working_data, + const DataFacade &facade, + const std::vector &phantom_nodes, + const std::vector &source_indices, + const std::vector &target_indices, + const bool calculate_distance, + const bool calculate_duration) { + (void)calculate_duration; // TODO: stub to use when computing durations become optional + const auto number_of_sources = source_indices.size(); const auto number_of_targets = target_indices.size(); const auto number_of_entries = number_of_sources * number_of_targets; std::vector weights_table(number_of_entries, INVALID_EDGE_WEIGHT); std::vector durations_table(number_of_entries, MAXIMAL_EDGE_DURATION); + std::vector distances_table; + std::vector middle_nodes_table(number_of_entries, SPECIAL_NODEID); std::vector search_space_with_buckets; // Populate buckets with paths from all accessible nodes to destinations via backward searches - for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx) + for (std::uint32_t column_index = 0; column_index < target_indices.size(); ++column_index) { - const auto index = target_indices[column_idx]; + const auto index = target_indices[column_index]; const auto &phantom = phantom_nodes[index]; engine_working_data.InitializeOrClearManyToManyThreadLocalStorage( @@ -184,7 +350,8 @@ std::vector manyToManySearch(SearchEngineData &engi // Explore search space while (!query_heap.Empty()) { - backwardRoutingStep(facade, column_idx, query_heap, search_space_with_buckets, phantom); + backwardRoutingStep( + facade, column_index, query_heap, search_space_with_buckets, phantom); } } @@ -192,32 +359,49 @@ std::vector manyToManySearch(SearchEngineData &engi std::sort(search_space_with_buckets.begin(), search_space_with_buckets.end()); // Find shortest paths from sources to all accessible nodes - for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx) + for (std::uint32_t row_index = 0; row_index < source_indices.size(); ++row_index) { - const auto index = source_indices[row_idx]; - const auto &phantom = phantom_nodes[index]; + const auto source_index = source_indices[row_index]; + const auto &source_phantom = phantom_nodes[source_index]; // Clear heap and insert source nodes engine_working_data.InitializeOrClearManyToManyThreadLocalStorage( facade.GetNumberOfNodes()); auto &query_heap = *(engine_working_data.many_to_many_heap); - insertSourceInHeap(query_heap, phantom); + insertSourceInHeap(query_heap, source_phantom); // Explore search space while (!query_heap.Empty()) { forwardRoutingStep(facade, - row_idx, + row_index, number_of_targets, query_heap, search_space_with_buckets, weights_table, durations_table, - phantom); + middle_nodes_table, + source_phantom); + } + + if (calculate_distance) + { + distances_table.resize(number_of_entries, INVALID_EDGE_DISTANCE); + calculateDistances(query_heap, + facade, + phantom_nodes, + target_indices, + row_index, + source_index, + source_phantom, + number_of_targets, + search_space_with_buckets, + distances_table, + middle_nodes_table); } } - return durations_table; + return std::make_pair(durations_table, distances_table); } } // namespace routing_algorithms diff --git a/src/engine/routing_algorithms/many_to_many_mld.cpp b/src/engine/routing_algorithms/many_to_many_mld.cpp index df9010bfbef..91033abd7e3 100644 --- a/src/engine/routing_algorithms/many_to_many_mld.cpp +++ b/src/engine/routing_algorithms/many_to_many_mld.cpp @@ -1,5 +1,5 @@ #include "engine/routing_algorithms/many_to_many.hpp" -#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/routing_algorithms/routing_base_mld.hpp" #include #include @@ -19,22 +19,8 @@ namespace routing_algorithms namespace mld { -template -inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, - const NodeID node, - const PhantomNode &phantom_node) -{ - auto highest_diffrent_level = [&partition, node](const SegmentID &phantom_node) { - if (phantom_node.enabled) - return partition.GetHighestDifferentLevel(phantom_node.id, node); - return INVALID_LEVEL_ID; - }; - - const auto node_level = std::min(highest_diffrent_level(phantom_node.forward_segment_id), - highest_diffrent_level(phantom_node.reverse_segment_id)); - - return node_level; -} +using PackedEdge = std::tuple; +using PackedPath = std::vector; template inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, @@ -50,38 +36,6 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, return node_level; } -template -inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, - NodeID node, - const std::vector &phantom_nodes, - const std::size_t phantom_index, - const std::vector &phantom_indices) -{ - auto min_level = [&partition, node](const PhantomNode &phantom_node) { - - const auto &forward_segment = phantom_node.forward_segment_id; - const auto forward_level = - forward_segment.enabled ? partition.GetHighestDifferentLevel(node, forward_segment.id) - : INVALID_LEVEL_ID; - - const auto &reverse_segment = phantom_node.reverse_segment_id; - const auto reverse_level = - reverse_segment.enabled ? partition.GetHighestDifferentLevel(node, reverse_segment.id) - : INVALID_LEVEL_ID; - - return std::min(forward_level, reverse_level); - }; - - // Get minimum level over all phantoms of the highest different level with respect to node - // This is equivalent to min_{∀ source, target} partition.GetQueryLevel(source, node, target) - auto result = min_level(phantom_nodes[phantom_index]); - for (const auto &index : phantom_indices) - { - result = std::min(result, min_level(phantom_nodes[index])); - } - return result; -} - template void relaxOutgoingEdges(const DataFacade &facade, const NodeID node, @@ -125,8 +79,10 @@ void relaxOutgoingEdges(const DataFacade &facade, { query_heap.Insert(to, to_weight, {node, true, to_duration}); } - else if (std::tie(to_weight, to_duration) < - std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration)) + else if (std::tie(to_weight, to_duration, node) < + std::tie(query_heap.GetKey(to), + query_heap.GetData(to).duration, + query_heap.GetData(to).parent)) { query_heap.GetData(to) = {node, true, to_duration}; query_heap.DecreaseKey(to, to_weight); @@ -155,8 +111,10 @@ void relaxOutgoingEdges(const DataFacade &facade, { query_heap.Insert(to, to_weight, {node, true, to_duration}); } - else if (std::tie(to_weight, to_duration) < - std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration)) + else if (std::tie(to_weight, to_duration, node) < + std::tie(query_heap.GetKey(to), + query_heap.GetData(to).duration, + query_heap.GetData(to).parent)) { query_heap.GetData(to) = {node, true, to_duration}; query_heap.DecreaseKey(to, to_weight); @@ -172,7 +130,8 @@ void relaxOutgoingEdges(const DataFacade &facade, for (const auto edge : facade.GetBorderEdgeRange(level, node)) { const auto &data = facade.GetEdgeData(edge); - if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward) + if ((DIRECTION == FORWARD_DIRECTION) ? facade.IsForwardEdge(edge) + : facade.IsBackwardEdge(edge)) { const NodeID to = facade.GetTarget(edge); if (facade.ExcludeNode(to)) @@ -180,12 +139,16 @@ void relaxOutgoingEdges(const DataFacade &facade, continue; } - const auto edge_weight = data.weight; - const auto edge_duration = data.duration; + const auto turn_id = data.turn_id; + const auto node_id = DIRECTION == FORWARD_DIRECTION ? node : facade.GetTarget(edge); + const auto node_weight = facade.GetNodeWeight(node_id); + const auto node_duration = facade.GetNodeDuration(node_id); + const auto turn_weight = node_weight + facade.GetWeightPenaltyForEdgeID(turn_id); + const auto turn_duration = node_duration + facade.GetDurationPenaltyForEdgeID(turn_id); - BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); - const auto to_weight = weight + edge_weight; - const auto to_duration = duration + edge_duration; + BOOST_ASSERT_MSG(node_weight + turn_weight > 0, "edge weight is invalid"); + const auto to_weight = weight + turn_weight; + const auto to_duration = duration + turn_duration; // New Node discovered -> Add to Heap + Node Info Storage if (!query_heap.WasInserted(to)) @@ -193,8 +156,10 @@ void relaxOutgoingEdges(const DataFacade &facade, query_heap.Insert(to, to_weight, {node, false, to_duration}); } // Found a shorter Path -> Update weight and set new parent - else if (std::tie(to_weight, to_duration) < - std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration)) + else if (std::tie(to_weight, to_duration, node) < + std::tie(query_heap.GetKey(to), + query_heap.GetData(to).duration, + query_heap.GetData(to).parent)) { query_heap.GetData(to) = {node, false, to_duration}; query_heap.DecreaseKey(to, to_weight); @@ -207,14 +172,18 @@ void relaxOutgoingEdges(const DataFacade &facade, // Unidirectional multi-layer Dijkstra search for 1-to-N and N-to-1 matrices // template -std::vector oneToManySearch(SearchEngineData &engine_working_data, - const DataFacade &facade, - const std::vector &phantom_nodes, - std::size_t phantom_index, - const std::vector &phantom_indices) +std::pair, std::vector> +oneToManySearch(SearchEngineData &engine_working_data, + const DataFacade &facade, + const std::vector &phantom_nodes, + std::size_t phantom_index, + const std::vector &phantom_indices, + const bool calculate_distance) { std::vector weights(phantom_indices.size(), INVALID_EDGE_WEIGHT); std::vector durations(phantom_indices.size(), MAXIMAL_EDGE_DURATION); + std::vector distances_table; + std::vector middle_nodes_table(phantom_indices.size(), SPECIAL_NODEID); // Collect destination (source) nodes into a map std::unordered_multimap> @@ -282,6 +251,7 @@ std::vector oneToManySearch(SearchEngineData &engine_wo { weights[index] = path_weight; durations[index] = path_duration; + middle_nodes_table[index] = node; } // Remove node from destinations list @@ -294,21 +264,36 @@ std::vector oneToManySearch(SearchEngineData &engine_wo } }; - // Check a single path result and insert adjacent nodes into heap auto insert_node = [&](NodeID node, EdgeWeight initial_weight, EdgeDuration initial_duration) { // Update single node paths update_values(node, initial_weight, initial_duration); + query_heap.Insert(node, initial_weight, {node, initial_duration}); + // Place adjacent nodes into heap for (auto edge : facade.GetAdjacentEdgeRange(node)) { const auto &data = facade.GetEdgeData(edge); - if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward) + const auto to = facade.GetTarget(edge); + + if (facade.ExcludeNode(to)) + { + continue; + } + + if ((DIRECTION == FORWARD_DIRECTION ? facade.IsForwardEdge(edge) + : facade.IsBackwardEdge(edge)) && + !query_heap.WasInserted(to)) { - query_heap.Insert(facade.GetTarget(edge), - data.weight + initial_weight, - {node, data.duration + initial_duration}); + const auto turn_id = data.turn_id; + const auto node_id = DIRECTION == FORWARD_DIRECTION ? node : to; + const auto edge_weight = initial_weight + facade.GetNodeWeight(node_id) + + facade.GetWeightPenaltyForEdgeID(turn_id); + const auto edge_duration = initial_duration + facade.GetNodeDuration(node_id) + + facade.GetDurationPenaltyForEdgeID(turn_id); + + query_heap.Insert(to, edge_weight, {node, edge_duration}); } } }; @@ -318,28 +303,35 @@ std::vector oneToManySearch(SearchEngineData &engine_wo if (DIRECTION == FORWARD_DIRECTION) { - if (phantom_node.IsValidForwardSource()) + { insert_node(phantom_node.forward_segment_id.id, -phantom_node.GetForwardWeightPlusOffset(), -phantom_node.GetForwardDuration()); + } if (phantom_node.IsValidReverseSource()) + { insert_node(phantom_node.reverse_segment_id.id, -phantom_node.GetReverseWeightPlusOffset(), -phantom_node.GetReverseDuration()); + } } else if (DIRECTION == REVERSE_DIRECTION) { if (phantom_node.IsValidForwardTarget()) + { insert_node(phantom_node.forward_segment_id.id, phantom_node.GetForwardWeightPlusOffset(), phantom_node.GetForwardDuration()); + } if (phantom_node.IsValidReverseTarget()) + { insert_node(phantom_node.reverse_segment_id.id, phantom_node.GetReverseWeightPlusOffset(), phantom_node.GetReverseDuration()); + } } } @@ -364,7 +356,127 @@ std::vector oneToManySearch(SearchEngineData &engine_wo phantom_indices); } - return durations; + if (calculate_distance) + { + // Initialize unpacking heaps + engine_working_data.InitializeOrClearFirstThreadLocalStorage( + facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1); + + distances_table.resize(phantom_indices.size(), INVALID_EDGE_DISTANCE); + + for (unsigned location = 0; location < phantom_indices.size(); ++location) + { + // Get the "middle" node that is the last node of a path + const NodeID middle_node_id = middle_nodes_table[location]; + if (middle_node_id == SPECIAL_NODEID) // takes care of one-ways + { + continue; + } + + // Retrieve the packed path from the heap + PackedPath packed_path = mld::retrievePackedPathFromSingleManyToManyHeap( + query_heap, middle_node_id); + + // ... and reverse it to have packed edges in the correct order, + if (DIRECTION == FORWARD_DIRECTION) + { + std::reverse(packed_path.begin(), packed_path.end()); + } + + // ... unpack path + auto &forward_heap = *engine_working_data.forward_heap_1; + auto &reverse_heap = *engine_working_data.reverse_heap_1; + EdgeWeight weight = INVALID_EDGE_WEIGHT; + std::vector unpacked_nodes; + std::vector unpacked_edges; + + std::tie(weight, unpacked_nodes, unpacked_edges) = + unpackPathAndCalculateDistance(engine_working_data, + facade, + forward_heap, + reverse_heap, + DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS, + INVALID_EDGE_WEIGHT, + packed_path, + middle_node_id, + phantom_nodes, + phantom_index, + phantom_indices); + + // Accumulate the path length without the last node + auto annotation = 0.0; + + BOOST_ASSERT(!unpacked_nodes.empty()); + for (auto node = unpacked_nodes.begin(), last_node = std::prev(unpacked_nodes.end()); + node != last_node; + ++node) + { + annotation += computeEdgeDistance(facade, *node); + } + + // ... and add negative source and positive target offsets + // ⚠ for REVERSE_DIRECTION original source and target phantom nodes are swapped + // Get source and target phantom nodes + // * 1-to-N: source is a single index, target is the corresponding from the indices list + // * N-to-1: source is the corresponding from the indices list, target is a single index + auto source_phantom_index = phantom_index; + auto target_phantom_index = phantom_indices[location]; + if (DIRECTION == REVERSE_DIRECTION) + { + std::swap(source_phantom_index, target_phantom_index); + } + const auto &source_phantom = phantom_nodes[source_phantom_index]; + const auto &target_phantom = phantom_nodes[target_phantom_index]; + const NodeID source_node = unpacked_nodes.front(); + const NodeID target_node = unpacked_nodes.back(); + + EdgeDistance source_offset = 0., target_offset = 0.; + if (source_phantom.IsValidForwardSource() && + source_phantom.forward_segment_id.id == source_node) + { + // ............ <-- calculateEGBAnnotation returns distance from 0 + // to 3 + // -->s <-- subtract offset to start at source + // ......... <-- want this distance as result + // entry 0---1---2---3--- <-- 3 is exit node + source_offset = source_phantom.GetForwardDistance(); + } + else if (source_phantom.IsValidReverseSource() && + source_phantom.reverse_segment_id.id == source_node) + { + // ............ <-- calculateEGBAnnotation returns distance from 0 to 3 + // s<------- <-- subtract offset to start at source + // ... <-- want this distance + // entry 0---1---2---3 <-- 3 is exit node + source_offset = source_phantom.GetReverseDistance(); + } + if (target_phantom.IsValidForwardTarget() && + target_phantom.forward_segment_id.id == target_node) + { + // ............ <-- calculateEGBAnnotation returns distance from 0 + // to 3 + // ++>t <-- add offset to get to target + // ................ <-- want this distance as result + // entry 0---1---2---3--- <-- 3 is exit node + target_offset = target_phantom.GetForwardDistance(); + } + else if (target_phantom.IsValidReverseTarget() && + target_phantom.reverse_segment_id.id == target_node) + { + // ............ <-- calculateEGBAnnotation returns distance from 0 + // to 3 + // <++t <-- add offset to get from target + // ................ <-- want this distance as result + // entry 0---1---2---3--- <-- 3 is exit node + target_offset = target_phantom.GetReverseDistance(); + } + + distances_table[location] = -source_offset + annotation + target_offset; + } + } + + return std::make_pair(durations, distances_table); } // @@ -379,6 +491,7 @@ void forwardRoutingStep(const DataFacade &facade, const std::vector &search_space_with_buckets, std::vector &weights_table, std::vector &durations_table, + std::vector &middle_nodes_table, const PhantomNode &phantom_node) { const auto node = query_heap.DeleteMin(); @@ -415,6 +528,7 @@ void forwardRoutingStep(const DataFacade &facade, { current_weight = new_weight; current_duration = new_duration; + middle_nodes_table[location] = node; } } @@ -432,9 +546,12 @@ void backwardRoutingStep(const DataFacade &facade, const auto node = query_heap.DeleteMin(); const auto target_weight = query_heap.GetKey(node); const auto target_duration = query_heap.GetData(node).duration; + const auto parent = query_heap.GetData(node).parent; + const auto from_clique_arc = query_heap.GetData(node).from_clique_arc; // Store settled nodes in search space bucket - search_space_with_buckets.emplace_back(node, column_idx, target_weight, target_duration); + search_space_with_buckets.emplace_back( + node, parent, from_clique_arc, column_idx, target_weight, target_duration); const auto &partition = facade.GetMultiLevelPartition(); const auto maximal_level = partition.GetNumberOfLevels() - 1; @@ -444,11 +561,225 @@ void backwardRoutingStep(const DataFacade &facade, } template -std::vector manyToManySearch(SearchEngineData &engine_working_data, - const DataFacade &facade, - const std::vector &phantom_nodes, - const std::vector &source_indices, - const std::vector &target_indices) +void retrievePackedPathFromSearchSpace(NodeID middle_node_id, + const unsigned column_idx, + const std::vector &search_space_with_buckets, + PackedPath &path) +{ + auto bucket_list = std::equal_range(search_space_with_buckets.begin(), + search_space_with_buckets.end(), + middle_node_id, + NodeBucket::ColumnCompare(column_idx)); + + BOOST_ASSERT_MSG(std::distance(bucket_list.first, bucket_list.second) == 1, + "The pointers are not pointing to the same element."); + + NodeID current_node_id = middle_node_id; + + while (bucket_list.first->parent_node != current_node_id && + bucket_list.first != search_space_with_buckets.end()) + { + const auto parent_node_id = bucket_list.first->parent_node; + + const auto from = DIRECTION == FORWARD_DIRECTION ? current_node_id : parent_node_id; + const auto to = DIRECTION == FORWARD_DIRECTION ? parent_node_id : current_node_id; + path.emplace_back(std::make_tuple(from, to, bucket_list.first->from_clique_arc)); + + current_node_id = parent_node_id; + bucket_list = std::equal_range(search_space_with_buckets.begin(), + search_space_with_buckets.end(), + current_node_id, + NodeBucket::ColumnCompare(column_idx)); + + BOOST_ASSERT_MSG(std::distance(bucket_list.first, bucket_list.second) == 1, + "The pointers are not pointing to the same element."); + } +} + +template +void calculateDistances(typename SearchEngineData::ManyToManyQueryHeap &query_heap, + const DataFacade &facade, + const std::vector &phantom_nodes, + const std::vector &target_indices, + const unsigned row_idx, + const std::size_t source_index, + const unsigned number_of_sources, + const unsigned number_of_targets, + const std::vector &search_space_with_buckets, + std::vector &distances_table, + const std::vector &middle_nodes_table, + SearchEngineData &engine_working_data) +{ + engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes(), + facade.GetMaxBorderNodeID() + 1); + + for (unsigned column_idx = 0; column_idx < number_of_targets; ++column_idx) + { + // Step 1: Get source and target phantom nodes that were used in the bucketed search + auto source_phantom_index = source_index; + auto target_phantom_index = target_indices[column_idx]; + const auto &source_phantom = phantom_nodes[source_phantom_index]; + const auto &target_phantom = phantom_nodes[target_phantom_index]; + + const auto location = DIRECTION == FORWARD_DIRECTION + ? row_idx * number_of_targets + column_idx + : row_idx + column_idx * number_of_sources; + + if (source_phantom_index == target_phantom_index) + { + distances_table[location] = 0.0; + continue; + } + + NodeID middle_node_id = middle_nodes_table[location]; + + if (middle_node_id == SPECIAL_NODEID) // takes care of one-ways + { + distances_table[location] = INVALID_EDGE_DISTANCE; + continue; + } + + // Step 2: Find path from source to middle node + PackedPath packed_path = + mld::retrievePackedPathFromSingleManyToManyHeap(query_heap, middle_node_id); + + if (DIRECTION == FORWARD_DIRECTION) + { + std::reverse(packed_path.begin(), packed_path.end()); + } + + auto &forward_heap = *engine_working_data.forward_heap_1; + auto &reverse_heap = *engine_working_data.reverse_heap_1; + EdgeWeight weight = INVALID_EDGE_WEIGHT; + std::vector unpacked_nodes_from_source; + std::vector unpacked_edges; + std::tie(weight, unpacked_nodes_from_source, unpacked_edges) = + unpackPathAndCalculateDistance(engine_working_data, + facade, + forward_heap, + reverse_heap, + DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS, + INVALID_EDGE_WEIGHT, + packed_path, + middle_node_id, + source_phantom); + + // Step 3: Find path from middle to target node + packed_path.clear(); + retrievePackedPathFromSearchSpace( + middle_node_id, column_idx, search_space_with_buckets, packed_path); + + if (DIRECTION == REVERSE_DIRECTION) + { + std::reverse(packed_path.begin(), packed_path.end()); + } + + std::vector unpacked_nodes_to_target; + std::tie(weight, unpacked_nodes_to_target, unpacked_edges) = + unpackPathAndCalculateDistance(engine_working_data, + facade, + forward_heap, + reverse_heap, + DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS, + INVALID_EDGE_WEIGHT, + packed_path, + middle_node_id, + target_phantom); + + if (DIRECTION == REVERSE_DIRECTION) + { + std::swap(unpacked_nodes_to_target, unpacked_nodes_from_source); + } + + // Step 4: Compute annotation value along the path nodes without the target node + auto annotation = 0.0; + + for (auto node = unpacked_nodes_from_source.begin(), + last_node = std::prev(unpacked_nodes_from_source.end()); + node != last_node; + ++node) + { + annotation += computeEdgeDistance(facade, *node); + } + + for (auto node = unpacked_nodes_to_target.begin(), + last_node = std::prev(unpacked_nodes_to_target.end()); + node != last_node; + ++node) + { + annotation += computeEdgeDistance(facade, *node); + } + + // Step 5: Get phantom node offsets and compute the annotation value + EdgeDistance source_offset = 0., target_offset = 0.; + { + // ⚠ for REVERSE_DIRECTION original source and target phantom nodes are swapped + if (DIRECTION == REVERSE_DIRECTION) + { + std::swap(source_phantom_index, target_phantom_index); + } + const auto &source_phantom = phantom_nodes[source_phantom_index]; + const auto &target_phantom = phantom_nodes[target_phantom_index]; + + NodeID source_node = unpacked_nodes_from_source.front(); + NodeID target_node = unpacked_nodes_to_target.back(); + + if (source_phantom.IsValidForwardSource() && + source_phantom.forward_segment_id.id == source_node) + { + // ............ <-- calculateEGBAnnotation returns distance from 0 + // to 3 + // -->s <-- subtract offset to start at source + // ......... <-- want this distance as result + // entry 0---1---2---3--- <-- 3 is exit node + source_offset = source_phantom.GetForwardDistance(); + } + else if (source_phantom.IsValidReverseSource() && + source_phantom.reverse_segment_id.id == source_node) + { + // ............ <-- calculateEGBAnnotation returns distance from 0 to 3 + // s<------- <-- subtract offset to start at source + // ... <-- want this distance + // entry 0---1---2---3 <-- 3 is exit node + source_offset = source_phantom.GetReverseDistance(); + } + + if (target_phantom.IsValidForwardTarget() && + target_phantom.forward_segment_id.id == target_node) + { + // ............ <-- calculateEGBAnnotation returns distance from 0 + // to 3 + // ++>t <-- add offset to get to target + // ................ <-- want this distance as result + // entry 0---1---2---3--- <-- 3 is exit node + target_offset = target_phantom.GetForwardDistance(); + } + else if (target_phantom.IsValidReverseTarget() && + target_phantom.reverse_segment_id.id == target_node) + { + // ............ <-- calculateEGBAnnotation returns distance from 0 + // to 3 + // <++t <-- add offset to get from target + // ................ <-- want this distance as result + // entry 0---1---2---3--- <-- 3 is exit node + target_offset = target_phantom.GetReverseDistance(); + } + } + + distances_table[location] = -source_offset + annotation + target_offset; + } +} + +template +std::pair, std::vector> +manyToManySearch(SearchEngineData &engine_working_data, + const DataFacade &facade, + const std::vector &phantom_nodes, + const std::vector &source_indices, + const std::vector &target_indices, + const bool calculate_distance) { const auto number_of_sources = source_indices.size(); const auto number_of_targets = target_indices.size(); @@ -456,6 +787,8 @@ std::vector manyToManySearch(SearchEngineData &engine_w std::vector weights_table(number_of_entries, INVALID_EDGE_WEIGHT); std::vector durations_table(number_of_entries, MAXIMAL_EDGE_DURATION); + std::vector distances_table; + std::vector middle_nodes_table(number_of_entries, SPECIAL_NODEID); std::vector search_space_with_buckets; @@ -463,22 +796,22 @@ std::vector manyToManySearch(SearchEngineData &engine_w for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx) { const auto index = target_indices[column_idx]; - const auto &phantom = phantom_nodes[index]; + const auto &target_phantom = phantom_nodes[index]; engine_working_data.InitializeOrClearManyToManyThreadLocalStorage( facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1); auto &query_heap = *(engine_working_data.many_to_many_heap); if (DIRECTION == FORWARD_DIRECTION) - insertTargetInHeap(query_heap, phantom); + insertTargetInHeap(query_heap, target_phantom); else - insertSourceInHeap(query_heap, phantom); + insertSourceInHeap(query_heap, target_phantom); // explore search space while (!query_heap.Empty()) { backwardRoutingStep( - facade, column_idx, query_heap, search_space_with_buckets, phantom); + facade, column_idx, query_heap, search_space_with_buckets, target_phantom); } } @@ -488,18 +821,19 @@ std::vector manyToManySearch(SearchEngineData &engine_w // Find shortest paths from sources to all accessible nodes for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx) { - const auto index = source_indices[row_idx]; - const auto &phantom = phantom_nodes[index]; + const auto source_index = source_indices[row_idx]; + const auto &source_phantom = phantom_nodes[source_index]; // Clear heap and insert source nodes engine_working_data.InitializeOrClearManyToManyThreadLocalStorage( facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1); + auto &query_heap = *(engine_working_data.many_to_many_heap); if (DIRECTION == FORWARD_DIRECTION) - insertSourceInHeap(query_heap, phantom); + insertSourceInHeap(query_heap, source_phantom); else - insertTargetInHeap(query_heap, phantom); + insertTargetInHeap(query_heap, source_phantom); // Explore search space while (!query_heap.Empty()) @@ -512,11 +846,29 @@ std::vector manyToManySearch(SearchEngineData &engine_w search_space_with_buckets, weights_table, durations_table, - phantom); + middle_nodes_table, + source_phantom); + } + + if (calculate_distance) + { + distances_table.resize(number_of_entries, INVALID_EDGE_DISTANCE); + calculateDistances(query_heap, + facade, + phantom_nodes, + target_indices, // source_indices + row_idx, + source_index, + number_of_sources, + number_of_targets, + search_space_with_buckets, + distances_table, + middle_nodes_table, + engine_working_data); } } - return durations_table; + return std::make_pair(durations_table, distances_table); } } // namespace mld @@ -534,32 +886,54 @@ std::vector manyToManySearch(SearchEngineData &engine_w // then search is performed on a reversed graph with phantom nodes with flipped roles and // returning a transposed matrix. template <> -std::vector manyToManySearch(SearchEngineData &engine_working_data, - const DataFacade &facade, - const std::vector &phantom_nodes, - const std::vector &source_indices, - const std::vector &target_indices) +std::pair, std::vector> +manyToManySearch(SearchEngineData &engine_working_data, + const DataFacade &facade, + const std::vector &phantom_nodes, + const std::vector &source_indices, + const std::vector &target_indices, + const bool calculate_distance, + const bool calculate_duration) { + (void)calculate_duration; // flag stub to use for calculating distances in matrix in mld in the + // future + if (source_indices.size() == 1) { // TODO: check if target_indices.size() == 1 and do a bi-directional search - return mld::oneToManySearch( - engine_working_data, facade, phantom_nodes, source_indices.front(), target_indices); + return mld::oneToManySearch(engine_working_data, + facade, + phantom_nodes, + source_indices.front(), + target_indices, + calculate_distance); } if (target_indices.size() == 1) { - return mld::oneToManySearch( - engine_working_data, facade, phantom_nodes, target_indices.front(), source_indices); + return mld::oneToManySearch(engine_working_data, + facade, + phantom_nodes, + target_indices.front(), + source_indices, + calculate_distance); } if (target_indices.size() < source_indices.size()) { - return mld::manyToManySearch( - engine_working_data, facade, phantom_nodes, target_indices, source_indices); + return mld::manyToManySearch(engine_working_data, + facade, + phantom_nodes, + target_indices, + source_indices, + calculate_distance); } - return mld::manyToManySearch( - engine_working_data, facade, phantom_nodes, source_indices, target_indices); + return mld::manyToManySearch(engine_working_data, + facade, + phantom_nodes, + source_indices, + target_indices, + calculate_distance); } } // namespace routing_algorithms diff --git a/src/engine/routing_algorithms/routing_base_ch.cpp b/src/engine/routing_algorithms/routing_base_ch.cpp index 4bdd0a73c18..695ebe9234a 100644 --- a/src/engine/routing_algorithms/routing_base_ch.cpp +++ b/src/engine/routing_algorithms/routing_base_ch.cpp @@ -59,6 +59,24 @@ void retrievePackedPathFromSingleHeap(const SearchEngineData::QueryHe } } +void retrievePackedPathFromSingleManyToManyHeap( + const SearchEngineData::ManyToManyQueryHeap &search_heap, + const NodeID middle_node_id, + std::vector &packed_path) +{ + NodeID current_node_id = middle_node_id; + // all initial nodes will have itself as parent, or a node not in the heap + // in case of a core search heap. We need a distinction between core entry nodes + // and start nodes since otherwise start node specific code that assumes + // node == node.parent (e.g. the loop code) might get actived. + while (current_node_id != search_heap.GetData(current_node_id).parent && + search_heap.WasInserted(search_heap.GetData(current_node_id).parent)) + { + current_node_id = search_heap.GetData(current_node_id).parent; + packed_path.emplace_back(current_node_id); + } +} + // assumes that heaps are already setup correctly. // ATTENTION: This only works if no additional offset is supplied next to the Phantom Node // Offsets. diff --git a/src/engine/routing_algorithms/tile_turns.cpp b/src/engine/routing_algorithms/tile_turns.cpp index 17ea540e3df..d8d28523d22 100644 --- a/src/engine/routing_algorithms/tile_turns.cpp +++ b/src/engine/routing_algorithms/tile_turns.cpp @@ -101,7 +101,6 @@ std::vector generateTurns(const datafacade &facade, // w // uv is the "approach" // vw is the "exit" - // Look at every node in the directed graph we created for (const auto &startnode : sorted_startnodes) { @@ -146,42 +145,8 @@ std::vector generateTurns(const datafacade &facade, const auto &data = facade.GetEdgeData(edge_based_edge_id); // Now, calculate the sum of the weight of all the segments. - const auto &geometry = - edge_based_node_info.find(approachedge.edge_based_node_id)->second; - EdgeWeight sum_node_weight = 0; - EdgeDuration sum_node_duration = 0; - if (geometry.is_geometry_forward) - { - const auto approach_weight = - facade.GetUncompressedForwardWeights(geometry.packed_geometry_id); - const auto approach_duration = - facade.GetUncompressedForwardDurations(geometry.packed_geometry_id); - sum_node_weight = std::accumulate( - approach_weight.begin(), approach_weight.end(), EdgeWeight{0}); - sum_node_duration = std::accumulate( - approach_duration.begin(), approach_duration.end(), EdgeDuration{0}); - } - else - { - const auto approach_weight = - facade.GetUncompressedReverseWeights(geometry.packed_geometry_id); - const auto approach_duration = - facade.GetUncompressedReverseDurations(geometry.packed_geometry_id); - sum_node_weight = std::accumulate( - approach_weight.begin(), approach_weight.end(), EdgeWeight{0}); - sum_node_duration = std::accumulate( - approach_duration.begin(), approach_duration.end(), EdgeDuration{0}); - } - - // The edge.weight is the whole edge weight, which includes the turn - // cost. - // The turn cost is the edge.weight minus the sum of the individual road - // segment weights. This might not be 100% accurate, because some - // intersections include stop signs, traffic signals and other - // penalties, but at this stage, we can't divide those out, so we just - // treat the whole lot as the "turn cost" that we'll stick on the map. - const auto turn_weight = data.weight - sum_node_weight; - const auto turn_duration = data.duration - sum_node_duration; + const auto turn_weight = facade.GetWeightPenaltyForEdgeID(data.turn_id); + const auto turn_duration = facade.GetDurationPenaltyForEdgeID(data.turn_id); const auto turn_instruction = facade.GetTurnInstructionForEdgeID(data.turn_id); // Find the three nodes that make up the turn movement) diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index 2db06188b9c..8d77e20743d 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -107,6 +107,13 @@ void EdgeBasedGraphFactory::GetEdgeBasedNodeWeights(std::vector &out swap(m_edge_based_node_weights, output_node_weights); } +void EdgeBasedGraphFactory::GetEdgeBasedNodeDurations( + std::vector &output_node_durations) +{ + using std::swap; // Koenig swap + swap(m_edge_based_node_durations, output_node_durations); +} + std::uint32_t EdgeBasedGraphFactory::GetConnectivityChecksum() const { return m_connectivity_checksum; @@ -138,9 +145,16 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N BOOST_ASSERT(nbe_to_ebn_mapping[edge_id_1] != SPECIAL_NODEID || nbe_to_ebn_mapping[edge_id_2] != SPECIAL_NODEID); + // ⚠ Use the sign bit of node weights to distinguish oneway streets: + // * MSB is set - a node corresponds to a one-way street + // * MSB is clear - a node corresponds to a bidirectional street + // Before using node weights data values must be adjusted: + // * in contraction if MSB is set the node weight is INVALID_EDGE_WEIGHT. + // This adjustment is needed to enforce loop creation for oneways. + // * in other cases node weights must be masked with 0x7fffffff to clear MSB if (nbe_to_ebn_mapping[edge_id_1] != SPECIAL_NODEID && nbe_to_ebn_mapping[edge_id_2] == SPECIAL_NODEID) - m_edge_based_node_weights[nbe_to_ebn_mapping[edge_id_1]] = INVALID_EDGE_WEIGHT; + m_edge_based_node_weights[nbe_to_ebn_mapping[edge_id_1]] |= 0x80000000; BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_1) == m_compressed_edge_container.HasEntryForID(edge_id_2)); @@ -278,6 +292,7 @@ unsigned EdgeBasedGraphFactory::LabelEdgeBasedNodes() // heuristic: node-based graph node is a simple intersection with four edges // (edge-based nodes) m_edge_based_node_weights.reserve(4 * m_node_based_graph.GetNumberOfNodes()); + m_edge_based_node_durations.reserve(4 * m_node_based_graph.GetNumberOfNodes()); nbe_to_ebn_mapping.resize(m_node_based_graph.GetEdgeCapacity(), SPECIAL_NODEID); // renumber edge based node of outgoing edges @@ -294,6 +309,7 @@ unsigned EdgeBasedGraphFactory::LabelEdgeBasedNodes() } m_edge_based_node_weights.push_back(edge_data.weight); + m_edge_based_node_durations.push_back(edge_data.duration); BOOST_ASSERT(numbered_edges_count < m_node_based_graph.GetNumberOfEdges()); nbe_to_ebn_mapping[current_edge] = numbered_edges_count; @@ -387,8 +403,10 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re segregated_edges.count(eid) > 0; const auto ebn_weight = m_edge_based_node_weights[nbe_to_ebn_mapping[eid]]; - BOOST_ASSERT(ebn_weight == INVALID_EDGE_WEIGHT || ebn_weight == edge_data.weight); + BOOST_ASSERT((ebn_weight & 0x7fffffff) == edge_data.weight); m_edge_based_node_weights.push_back(ebn_weight); + m_edge_based_node_durations.push_back( + m_edge_based_node_durations[nbe_to_ebn_mapping[eid]]); edge_based_node_id++; progress.PrintStatus(progress_counter++); @@ -397,6 +415,7 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re BOOST_ASSERT(m_edge_based_node_segments.size() == m_edge_based_node_is_startpoint.size()); BOOST_ASSERT(m_number_of_edge_based_nodes == m_edge_based_node_weights.size()); + BOOST_ASSERT(m_number_of_edge_based_nodes == m_edge_based_node_durations.size()); util::Log() << "Generated " << m_number_of_edge_based_nodes << " nodes (" << way_restriction_map.NumberOfDuplicatedNodes() diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index 064fc59d055..f62bb1b39cc 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -241,6 +241,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) util::DeallocatingVector edge_based_edge_list; std::vector node_is_startpoint; std::vector edge_based_node_weights; + std::vector edge_based_node_durations; std::uint32_t ebg_connectivity_checksum = 0; // Create a node-based graph from the OSRM file @@ -320,6 +321,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) edge_based_node_segments, node_is_startpoint, edge_based_node_weights, + edge_based_node_durations, edge_based_edge_list, ebg_connectivity_checksum); @@ -343,8 +345,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) util::Log() << "Saving edge-based node weights to file."; TIMER_START(timer_write_node_weights); - extractor::files::writeEdgeBasedNodeWeights(config.GetPath(".osrm.enw"), - edge_based_node_weights); + extractor::files::writeEdgeBasedNodeWeightsDurations( + config.GetPath(".osrm.enw"), edge_based_node_weights, edge_based_node_durations); TIMER_STOP(timer_write_node_weights); util::Log() << "Done writing. (" << TIMER_SEC(timer_write_node_weights) << ")"; @@ -733,6 +735,7 @@ EdgeID Extractor::BuildEdgeExpandedGraph( std::vector &edge_based_node_segments, std::vector &node_is_startpoint, std::vector &edge_based_node_weights, + std::vector &edge_based_node_durations, util::DeallocatingVector &edge_based_edge_list, std::uint32_t &connectivity_checksum) { @@ -782,6 +785,7 @@ EdgeID Extractor::BuildEdgeExpandedGraph( edge_based_graph_factory.GetEdgeBasedNodeSegments(edge_based_node_segments); edge_based_graph_factory.GetStartPointMarkers(node_is_startpoint); edge_based_graph_factory.GetEdgeBasedNodeWeights(edge_based_node_weights); + edge_based_graph_factory.GetEdgeBasedNodeDurations(edge_based_node_durations); connectivity_checksum = edge_based_graph_factory.GetConnectivityChecksum(); return number_of_edge_based_nodes; diff --git a/src/nodejs/node_osrm.cpp b/src/nodejs/node_osrm.cpp index e98e251d28a..aa7e03851f6 100644 --- a/src/nodejs/node_osrm.cpp +++ b/src/nodejs/node_osrm.cpp @@ -263,7 +263,7 @@ NAN_METHOD(Engine::nearest) // // clang-format off /** - * Computes duration tables for the given locations. Allows for both symmetric and asymmetric + * Computes duration and distance tables for the given locations. Allows for both symmetric and asymmetric * tables. * * @name table @@ -274,17 +274,20 @@ NAN_METHOD(Engine::nearest) // * Can be `null` or an array of `[{value},{range}]` with `integer 0 .. 360,integer 0 .. 180`. * @param {Array} [options.radiuses] Limits the coordinate snapping to streets in the given radius in meters. Can be `null` (unlimited, default) or `double >= 0`. * @param {Array} [options.hints] Hints for the coordinate snapping. Array of base64 encoded strings. - * @param {Array} [options.sources] An array of `index` elements (`0 <= integer < #coordinates`) to - * use - * location with given index as source. Default is to use all. - * @param {Array} [options.destinations] An array of `index` elements (`0 <= integer < - * #coordinates`) to use location with given index as destination. Default is to use all. + * @param {Array} [options.sources] An array of `index` elements (`0 <= integer < #coordinates`) to use + * location with given index as source. Default is to use all. + * @param {Array} [options.destinations] An array of `index` elements (`0 <= integer < #coordinates`) to use location with given index as destination. Default is to use all. * @param {Array} [options.approaches] Keep waypoints on curb side. Can be `null` (unrestricted, default) or `curb`. + * @param {Array} [options.annotations] An array of the table types to return. Values can be `duration` or `distance` or both. If no annotations parameter is added, the default is to return the `durations` table. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned. + * @param {Function} callback * * @returns {Object} containing `durations`, `sources`, and `destinations`. * **`durations`**: array of arrays that stores the matrix in row-major order. `durations[i][j]` gives the travel time from the i-th waypoint to the j-th waypoint. * Values are given in seconds. + * **`distances`**: array of arrays that stores the matrix in row-major order. `distances[i][j]` gives the travel time from the i-th waypoint to the j-th waypoint. + * Values are given in meters. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or + * `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned. * **`sources`**: array of [`Ẁaypoint`](#waypoint) objects describing all sources in order. * **`destinations`**: array of [`Ẁaypoint`](#waypoint) objects describing all destinations in order. * @@ -299,6 +302,7 @@ NAN_METHOD(Engine::nearest) // * }; * osrm.table(options, function(err, response) { * console.log(response.durations); // array of arrays, matrix in row-major order + * console.log(response.distances); // array of arrays, matrix in row-major order (currently only implemented for CH router) * console.log(response.sources); // array of Waypoint objects * console.log(response.destinations); // array of Waypoint objects * }); diff --git a/src/partitioner/partitioner.cpp b/src/partitioner/partitioner.cpp index 06fa87c5d3a..c180893863e 100644 --- a/src/partitioner/partitioner.cpp +++ b/src/partitioner/partitioner.cpp @@ -144,6 +144,16 @@ int Partitioner::Run(const PartitionerConfig &config) renumber(node_data, permutation); extractor::files::writeNodeData(config.GetPath(".osrm.ebg_nodes"), node_data); } + { + std::vector node_weights; + std::vector node_durations; + extractor::files::readEdgeBasedNodeWeightsDurations( + config.GetPath(".osrm.enw"), node_weights, node_durations); + util::inplacePermutation(node_weights.begin(), node_weights.end(), permutation); + util::inplacePermutation(node_durations.begin(), node_durations.end(), permutation); + extractor::files::writeEdgeBasedNodeWeightsDurations( + config.GetPath(".osrm.enw"), node_weights, node_durations); + } { const auto &filename = config.GetPath(".osrm.maneuver_overrides"); std::vector maneuver_overrides; diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp index 7ae6937398b..bcdf0325291 100644 --- a/src/storage/storage.cpp +++ b/src/storage/storage.cpp @@ -554,7 +554,7 @@ void Storage::PopulateUpdatableData(const SharedDataIndex &index) { auto graph_view = make_multi_level_graph_view(index, "/mld/multilevelgraph"); std::uint32_t graph_connectivity_checksum = 0; - partitioner::files::readGraph( + customizer::files::readGraph( config.GetPath(".osrm.mldgr"), graph_view, graph_connectivity_checksum); auto turns_connectivity_checksum = diff --git a/src/tools/store.cpp b/src/tools/store.cpp index 5755e77dd66..cf9d965264d 100644 --- a/src/tools/store.cpp +++ b/src/tools/store.cpp @@ -1,6 +1,8 @@ +#include "storage/serialization.hpp" #include "storage/shared_memory.hpp" #include "storage/shared_monitor.hpp" #include "storage/storage.hpp" + #include "osrm/exception.hpp" #include "util/log.hpp" #include "util/meminfo.hpp" @@ -25,7 +27,7 @@ void deleteRegion(const storage::SharedRegionRegister::ShmKey key) } } -void listRegions() +void listRegions(bool show_blocks) { osrm::util::Log() << "name\tshm key\ttimestamp\tsize"; if (!storage::SharedMonitor::exists()) @@ -43,6 +45,23 @@ void listRegions() auto shm = osrm::storage::makeSharedMemory(region.shm_key); osrm::util::Log() << name << "\t" << static_cast(region.shm_key) << "\t" << region.timestamp << "\t" << shm->Size(); + + if (show_blocks) + { + using namespace storage; + auto memory = makeSharedMemory(region.shm_key); + io::BufferReader reader(reinterpret_cast(memory->Ptr()), memory->Size()); + + DataLayout layout; + serialization::read(reader, layout); + + std::vector block_names; + layout.List("", std::back_inserter(block_names)); + for (auto &name : block_names) + { + osrm::util::Log() << " " << name << " " << layout.GetBlockSize(name); + } + } } } @@ -79,6 +98,7 @@ bool generateDataStoreOptions(const int argc, int &max_wait, std::string &dataset_name, bool &list_datasets, + bool &list_blocks, bool &only_metric) { // declare a group of options that will be allowed only on command line @@ -109,12 +129,18 @@ bool generateDataStoreOptions(const int argc, ->default_value(false) ->implicit_value(true), "List all OSRM datasets currently in memory") // - ("only-metric", - boost::program_options::value(&only_metric) + ("list-blocks", + boost::program_options::value(&list_blocks) ->default_value(false) ->implicit_value(true), - "Only reload the metric data without updating the full dataset. This is an optimization " - "for traffic updates."); + "List all OSRM datasets currently in memory")( + "only-metric", + boost::program_options::value(&only_metric) + ->default_value(false) + ->implicit_value(true), + "Only reload the metric data without updating the full dataset. This is an " + "optimization " + "for traffic updates."); // hidden options, will be allowed on command line but will not be shown to the user boost::program_options::options_description hidden_options("Hidden options"); @@ -209,18 +235,26 @@ int main(const int argc, const char *argv[]) try int max_wait = -1; std::string dataset_name; bool list_datasets = false; + bool list_blocks = false; bool only_metric = false; - if (!generateDataStoreOptions( - argc, argv, verbosity, base_path, max_wait, dataset_name, list_datasets, only_metric)) + if (!generateDataStoreOptions(argc, + argv, + verbosity, + base_path, + max_wait, + dataset_name, + list_datasets, + list_blocks, + only_metric)) { return EXIT_SUCCESS; } util::LogPolicy::GetInstance().SetLevel(verbosity); - if (list_datasets) + if (list_datasets || list_blocks) { - listRegions(); + listRegions(list_blocks); return EXIT_SUCCESS; } diff --git a/src/updater/updater.cpp b/src/updater/updater.cpp index b9e5356fc3b..f4dc902a3e6 100644 --- a/src/updater/updater.cpp +++ b/src/updater/updater.cpp @@ -517,7 +517,6 @@ updateConditionalTurns(std::vector &turn_weight_penalties, { if (IsRestrictionValid(time_zone_handler, penalty)) { - std::cout << "Disabling: " << penalty.turn_offset << std::endl; turn_weight_penalties[penalty.turn_offset] = INVALID_TURN_PENALTY; updated_turns.push_back(penalty.turn_offset); } @@ -526,20 +525,20 @@ updateConditionalTurns(std::vector &turn_weight_penalties, } } -Updater::NumNodesAndEdges Updater::LoadAndUpdateEdgeExpandedGraph() const +EdgeID +Updater::LoadAndUpdateEdgeExpandedGraph(std::vector &edge_based_edge_list, + std::vector &node_weights, + std::uint32_t &connectivity_checksum) const { - std::vector node_weights; - std::vector edge_based_edge_list; - std::uint32_t connectivity_checksum; - auto number_of_edge_based_nodes = Updater::LoadAndUpdateEdgeExpandedGraph( - edge_based_edge_list, node_weights, connectivity_checksum); - return std::make_tuple( - number_of_edge_based_nodes, std::move(edge_based_edge_list), connectivity_checksum); + std::vector node_durations(node_weights.size()); + return LoadAndUpdateEdgeExpandedGraph( + edge_based_edge_list, node_weights, node_durations, connectivity_checksum); } EdgeID Updater::LoadAndUpdateEdgeExpandedGraph(std::vector &edge_based_edge_list, std::vector &node_weights, + std::vector &node_durations, std::uint32_t &connectivity_checksum) const { TIMER_START(load_edges); @@ -548,6 +547,9 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector &e std::vector coordinates; extractor::PackedOSMIDs osm_node_ids; + extractor::files::readEdgeBasedNodeWeightsDurations( + config.GetPath(".osrm.enw"), node_weights, node_durations); + extractor::files::readEdgeBasedGraph(config.GetPath(".osrm.ebg"), number_of_edge_based_nodes, edge_based_edge_list, @@ -743,12 +745,12 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector &e accumulated_segment_data[updated_iter - updated_segments.begin()]; // Update the node-weight cache. This is the weight of the edge-based-node - // only, - // it doesn't include the turn. We may visit the same node multiple times, - // but - // we should always assign the same value here. - if (node_weights.size() > 0) - node_weights[edge.source] = new_weight; + // only, it doesn't include the turn. We may visit the same node multiple times, + // but we should always assign the same value here. + BOOST_ASSERT(edge.source < node_weights.size()); + node_weights[edge.source] = + node_weights[edge.source] & 0x80000000 ? new_weight | 0x80000000 : new_weight; + node_durations[edge.source] = new_duration; // We found a zero-speed edge, so we'll skip this whole edge-based-edge // which diff --git a/src/util/coordinate_calculation.cpp b/src/util/coordinate_calculation.cpp index ae968203d73..cb9adb10a9b 100644 --- a/src/util/coordinate_calculation.cpp +++ b/src/util/coordinate_calculation.cpp @@ -5,6 +5,8 @@ #include +#include + #include #include #include @@ -18,6 +20,44 @@ namespace util namespace coordinate_calculation { +namespace +{ +class CheapRulerContainer +{ + public: + CheapRulerContainer(const int number_of_rulers) + : cheap_ruler_cache(number_of_rulers, mapbox::cheap_ruler::CheapRuler(0)), + step(90.0 * COORDINATE_PRECISION / number_of_rulers) + { + for (int n = 0; n < number_of_rulers; n++) + { + cheap_ruler_cache[n] = mapbox::cheap_ruler::CheapRuler( + step * (n + 0.5) / COORDINATE_PRECISION, mapbox::cheap_ruler::CheapRuler::Meters); + } + }; + + mapbox::cheap_ruler::CheapRuler &getRuler(const FixedLatitude lat_1, const FixedLatitude lat_2) + { + auto lat = (lat_1 + lat_2) / util::FixedLatitude{2}; + return getRuler(lat); + } + + mapbox::cheap_ruler::CheapRuler &getRuler(const FixedLatitude lat) + { + BOOST_ASSERT(step > 2); + // the |lat| > 0 -> |lat|-1 > -1 -> (|lat|-1)/step > -1/step > -1/2 >= -1 -> bin >= 0 + std::size_t bin = (std::abs(static_cast(lat)) - 1) / step; + BOOST_ASSERT(bin < cheap_ruler_cache.size()); + return cheap_ruler_cache[bin]; + }; + + private: + std::vector cheap_ruler_cache; + const int step; +}; +static CheapRulerContainer cheap_ruler_container(1800); +} // namespace + // Does not project the coordinates! std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rhs) { @@ -32,6 +72,20 @@ std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rh return result; } +// Uses method described here: +// https://www.gpo.gov/fdsys/pkg/CFR-2005-title47-vol4/pdf/CFR-2005-title47-vol4-sec73-208.pdf +// should be within 0.1% or so of Vincenty method (assuming 19 buckets are enough) +// Should be more faster and more precise than Haversine +double fccApproximateDistance(const Coordinate coordinate_1, const Coordinate coordinate_2) +{ + const auto lon1 = static_cast(util::toFloating(coordinate_1.lon)); + const auto lat1 = static_cast(util::toFloating(coordinate_1.lat)); + const auto lon2 = static_cast(util::toFloating(coordinate_2.lon)); + const auto lat2 = static_cast(util::toFloating(coordinate_2.lat)); + return cheap_ruler_container.getRuler(coordinate_1.lat, coordinate_2.lat) + .distance({lon1, lat1}, {lon2, lat2}); +} + double haversineDistance(const Coordinate coordinate_1, const Coordinate coordinate_2) { auto lon1 = static_cast(coordinate_1.lon); @@ -423,6 +477,6 @@ double computeArea(const std::vector &polygon) return area / 2.; } -} // ns coordinate_calculation -} // ns util -} // ns osrm +} // namespace coordinate_calculation +} // namespace util +} // namespace osrm diff --git a/test/nodejs/constants.js b/test/nodejs/constants.js index bd3c454cb55..0726bc91c3d 100644 --- a/test/nodejs/constants.js +++ b/test/nodejs/constants.js @@ -10,7 +10,7 @@ exports.three_test_coordinates = [[7.41337, 43.72956], exports.two_test_coordinates = exports.three_test_coordinates.slice(0, 2) -exports.test_tile = {'at': [17059, 11948, 15], 'size': 168571}; +exports.test_tile = {'at': [17059, 11948, 15], 'size': 148750}; // Test files generated by the routing engine; check test/data if (process.env.OSRM_DATA_PATH !== undefined) { diff --git a/test/nodejs/table.js b/test/nodejs/table.js index 972550614d4..220added1b0 100644 --- a/test/nodejs/table.js +++ b/test/nodejs/table.js @@ -5,179 +5,228 @@ var mld_data_path = require('./constants').mld_data_path; var three_test_coordinates = require('./constants').three_test_coordinates; var two_test_coordinates = require('./constants').two_test_coordinates; - -test('table: distance table in Monaco', function(assert) { - assert.plan(11); +test('table: test annotations paramater combination', function(assert) { + assert.plan(12); var osrm = new OSRM(data_path); var options = { - coordinates: [three_test_coordinates[0], three_test_coordinates[1]] + coordinates: [three_test_coordinates[0], three_test_coordinates[1]], + annotations: ['distance'] }; osrm.table(options, function(err, table) { assert.ifError(err); - assert.ok(Array.isArray(table.durations), 'result must be an array'); - var row_count = table.durations.length; - for (var i = 0; i < row_count; ++i) { - var column = table.durations[i]; - var column_count = column.length; - assert.equal(row_count, column_count); - for (var j = 0; j < column_count; ++j) { - if (i == j) { - // check that diagonal is zero - assert.equal(0, column[j], 'diagonal must be zero'); - } else { - // everything else is non-zero - assert.notEqual(0, column[j], 'other entries must be non-zero'); - // and finite (not nan, inf etc.) - assert.ok(Number.isFinite(column[j]), 'distance is finite number'); - } - } - } - assert.equal(options.coordinates.length, row_count); + assert.ok(table['distances'], 'distances table result should exist'); + assert.notOk(table['durations'], 'durations table result should not exist'); }); -}); -test('table: distance table in Monaco with sources/destinations', function(assert) { - assert.plan(7); - var osrm = new OSRM(data_path); - var options = { + options = { coordinates: [three_test_coordinates[0], three_test_coordinates[1]], - sources: [0], - destinations: [0,1] + annotations: ['duration'] }; osrm.table(options, function(err, table) { assert.ifError(err); - assert.ok(Array.isArray(table.durations), 'result must be an array'); - var row_count = table.durations.length; - for (var i = 0; i < row_count; ++i) { - var column = table.durations[i]; - var column_count = column.length; - assert.equal(options.destinations.length, column_count); - for (var j = 0; j < column_count; ++j) { - if (i == j) { - // check that diagonal is zero - assert.equal(0, column[j], 'diagonal must be zero'); - } else { - // everything else is non-zero - assert.notEqual(0, column[j], 'other entries must be non-zero'); - // and finite (not nan, inf etc.) - assert.ok(Number.isFinite(column[j]), 'distance is finite number'); - } - } - } - assert.equal(options.sources.length, row_count); + assert.ok(table['durations'], 'durations table result should exist'); + assert.notOk(table['distances'], 'distances table result should not exist'); }); -}); -test('table: throws on invalid arguments', function(assert) { - assert.plan(14); - var osrm = new OSRM(data_path); - var options = {}; - assert.throws(function() { osrm.table(options); }, - /Two arguments required/); - options.coordinates = null; - assert.throws(function() { osrm.table(options, function() {}); }, - /Coordinates must be an array of \(lon\/lat\) pairs/); - options.coordinates = [three_test_coordinates[0]]; - assert.throws(function() { osrm.table(options, function(err, response) {}) }, - /At least two coordinates must be provided/); - options.coordinates = three_test_coordinates[0]; - assert.throws(function() { osrm.table(options, function(err, response) {}) }, - /Coordinates must be an array of \(lon\/lat\) pairs/); - options.coordinates = [three_test_coordinates[0][0], three_test_coordinates[0][1]]; - assert.throws(function() { osrm.table(options, function(err, response) {}) }, - /Coordinates must be an array of \(lon\/lat\) pairs/); - - options.coordinates = two_test_coordinates; - options.sources = true; - assert.throws(function() { osrm.table(options, function(err, response) {}) }, - /Sources must be an array of indices \(or undefined\)/); - options.sources = [0, 4]; - assert.throws(function() { osrm.table(options, function(err, response) {}) }, - /Source indices must be less than or equal to the number of coordinates/); - options.sources = [0.3, 1.1]; - assert.throws(function() { osrm.table(options, function(err, response) {}) }, - /Source must be an integer/); - - options.destinations = true; - delete options.sources; - assert.throws(function() { osrm.table(options, function(err, response) {}) }, - /Destinations must be an array of indices \(or undefined\)/); - options.destinations = [0, 4]; - assert.throws(function() { osrm.table(options, function(err, response) {}) }, - /Destination indices must be less than or equal to the number of coordinates/); - options.destinations = [0.3, 1.1]; - assert.throws(function() { osrm.table(options, function(err, response) {}) }, - /Destination must be an integer/); - - // does not throw: the following two have been changed in OSRM v5 - options.sources = [0, 1]; - delete options.destinations; - assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) }, - /Both sources and destinations need to be specified/); - options.destinations = [0, 1]; - assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) }, - /You can either specify sources and destinations, or coordinates/); - - assert.throws(function() { osrm.route({coordinates: two_test_coordinates, generate_hints: null}, function(err, route) {}) }, - /generate_hints must be of type Boolean/); -}); - -test('table: throws on invalid arguments', function(assert) { - assert.plan(1); - var osrm = new OSRM(data_path); - assert.throws(function() { osrm.table(null, function() {}); }, - /First arg must be an object/); -}); - -test('table: distance table in Monaco with hints', function(assert) { - assert.plan(5); - var osrm = new OSRM(data_path); - var options = { - coordinates: two_test_coordinates, - generate_hints: true // true is default but be explicit here + options = { + coordinates: [three_test_coordinates[0], three_test_coordinates[1]], + annotations: ['duration', 'distance'] }; osrm.table(options, function(err, table) { - console.log(table); assert.ifError(err); - - function assertHasHints(waypoint) { - assert.notStrictEqual(waypoint.hint, undefined); - } - - table.sources.map(assertHasHints); - table.destinations.map(assertHasHints); + assert.ok(table['durations'], 'durations table result should exist'); + assert.ok(table['distances'], 'distances table result should exist'); }); -}); -test('table: distance table in Monaco without hints', function(assert) { - assert.plan(5); - var osrm = new OSRM(data_path); - var options = { - coordinates: two_test_coordinates, - generate_hints: false // true is default + options = { + coordinates: [three_test_coordinates[0], three_test_coordinates[1]] }; osrm.table(options, function(err, table) { assert.ifError(err); + assert.ok(table['durations'], 'durations table result should exist'); + assert.notOk(table['distances'], 'distances table result should not exist'); + }); +}); - function assertHasNoHints(waypoint) { - assert.strictEqual(waypoint.hint, undefined); - } +var tables = ['distances', 'durations']; + +tables.forEach(function(annotation) { + test('table: ' + annotation + ' table in Monaco', function(assert) { + assert.plan(11); + var osrm = new OSRM(data_path); + var options = { + coordinates: [three_test_coordinates[0], three_test_coordinates[1]], + annotations: [annotation.slice(0,-1)] + }; + osrm.table(options, function(err, table) { + assert.ifError(err); + assert.ok(Array.isArray(table[annotation]), 'result must be an array'); + var row_count = table[annotation].length; + for (var i = 0; i < row_count; ++i) { + var column = table[annotation][i]; + var column_count = column.length; + assert.equal(row_count, column_count); + for (var j = 0; j < column_count; ++j) { + if (i == j) { + // check that diagonal is zero + assert.equal(0, column[j], 'diagonal must be zero'); + } else { + // everything else is non-zero + assert.notEqual(0, column[j], 'other entries must be non-zero'); + // and finite (not nan, inf etc.) + assert.ok(Number.isFinite(column[j]), 'distance is finite number'); + } + } + } + assert.equal(options.coordinates.length, row_count); + }); + }); - table.sources.map(assertHasNoHints); - table.destinations.map(assertHasNoHints); + test('table: ' + annotation + ' table in Monaco with sources/destinations', function(assert) { + assert.plan(7); + var osrm = new OSRM(data_path); + var options = { + coordinates: [three_test_coordinates[0], three_test_coordinates[1]], + sources: [0], + destinations: [0,1], + annotations: [annotation.slice(0,-1)] + }; + osrm.table(options, function(err, table) { + assert.ifError(err); + assert.ok(Array.isArray(table[annotation]), 'result must be an array'); + var row_count = table[annotation].length; + for (var i = 0; i < row_count; ++i) { + var column = table[annotation][i]; + var column_count = column.length; + assert.equal(options.destinations.length, column_count); + for (var j = 0; j < column_count; ++j) { + if (i == j) { + // check that diagonal is zero + assert.equal(0, column[j], 'diagonal must be zero'); + } else { + // everything else is non-zero + assert.notEqual(0, column[j], 'other entries must be non-zero'); + // and finite (not nan, inf etc.) + assert.ok(Number.isFinite(column[j]), 'distance is finite number'); + } + } + } + assert.equal(options.sources.length, row_count); + }); }); -}); -test('table: table in Monaco without motorways', function(assert) { - assert.plan(2); - var osrm = new OSRM({path: mld_data_path, algorithm: 'MLD'}); - var options = { - coordinates: two_test_coordinates, - exclude: ['motorway'] - }; - osrm.table(options, function(err, response) { - assert.ifError(err); - assert.equal(response.durations.length, 2); + test('table: ' + annotation + ' throws on invalid arguments', function(assert) { + assert.plan(14); + var osrm = new OSRM(data_path); + var options = {annotations: [annotation.slice(0,-1)]}; + assert.throws(function() { osrm.table(options); }, + /Two arguments required/); + options.coordinates = null; + assert.throws(function() { osrm.table(options, function() {}); }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + options.coordinates = [three_test_coordinates[0]]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /At least two coordinates must be provided/); + options.coordinates = three_test_coordinates[0]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + options.coordinates = [three_test_coordinates[0][0], three_test_coordinates[0][1]]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + + options.coordinates = two_test_coordinates; + options.sources = true; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Sources must be an array of indices \(or undefined\)/); + options.sources = [0, 4]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Source indices must be less than or equal to the number of coordinates/); + options.sources = [0.3, 1.1]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Source must be an integer/); + + options.destinations = true; + delete options.sources; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Destinations must be an array of indices \(or undefined\)/); + options.destinations = [0, 4]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Destination indices must be less than or equal to the number of coordinates/); + options.destinations = [0.3, 1.1]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Destination must be an integer/); + + // does not throw: the following two have been changed in OSRM v5 + options.sources = [0, 1]; + delete options.destinations; + assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) }, + /Both sources and destinations need to be specified/); + options.destinations = [0, 1]; + assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) }, + /You can either specify sources and destinations, or coordinates/); + + assert.throws(function() { osrm.route({coordinates: two_test_coordinates, generate_hints: null}, function(err, route) {}) }, + /generate_hints must be of type Boolean/); + }); + + test('table: throws on invalid arguments', function(assert) { + assert.plan(1); + var osrm = new OSRM(data_path); + assert.throws(function() { osrm.table(null, function() {}); }, + /First arg must be an object/); + }); + + test('table: ' + annotation + ' table in Monaco with hints', function(assert) { + assert.plan(5); + var osrm = new OSRM(data_path); + var options = { + coordinates: two_test_coordinates, + generate_hints: true, // true is default but be explicit here + annotations: [annotation.slice(0,-1)] + }; + osrm.table(options, function(err, table) { + assert.ifError(err); + + function assertHasHints(waypoint) { + assert.notStrictEqual(waypoint.hint, undefined); + } + + table.sources.map(assertHasHints); + table.destinations.map(assertHasHints); + }); + }); + + test('table: ' + annotation + ' table in Monaco without hints', function(assert) { + assert.plan(5); + var osrm = new OSRM(data_path); + var options = { + coordinates: two_test_coordinates, + generate_hints: false, // true is default + annotations: [annotation.slice(0,-1)] + }; + osrm.table(options, function(err, table) { + assert.ifError(err); + + function assertHasNoHints(waypoint) { + assert.strictEqual(waypoint.hint, undefined); + } + + table.sources.map(assertHasNoHints); + table.destinations.map(assertHasNoHints); + }); + }); + + test('table: ' + annotation + ' table in Monaco without motorways', function(assert) { + assert.plan(1); + var osrm = new OSRM({path: mld_data_path, algorithm: 'MLD'}); + var options = { + coordinates: two_test_coordinates, + exclude: ['motorway'], + annotations: [annotation.slice(0,-1)] + }; + osrm.table(options, function(err, response) { + assert.equal(response[annotation].length, 2); + }); }); }); diff --git a/third_party/cheap-ruler-cpp-2.5.4/.clang-format b/third_party/cheap-ruler-cpp-2.5.4/.clang-format new file mode 100644 index 00000000000..ff0f39e73bb --- /dev/null +++ b/third_party/cheap-ruler-cpp-2.5.4/.clang-format @@ -0,0 +1,18 @@ +Standard: Cpp11 +IndentWidth: 4 +AccessModifierOffset: -4 +UseTab: Never +BinPackParameters: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortBlocksOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +AlwaysBreakTemplateDeclarations: true +NamespaceIndentation: None +PointerBindsToType: true +SpacesInParentheses: false +BreakBeforeBraces: Attach +ColumnLimit: 100 +Cpp11BracedListStyle: false +SpacesBeforeTrailingComments: 1 diff --git a/third_party/cheap-ruler-cpp-2.5.4/.gitignore b/third_party/cheap-ruler-cpp-2.5.4/.gitignore new file mode 100644 index 00000000000..7117dfc1b4a --- /dev/null +++ b/third_party/cheap-ruler-cpp-2.5.4/.gitignore @@ -0,0 +1,2 @@ +build/ +mason_packages/ diff --git a/third_party/cheap-ruler-cpp-2.5.4/.travis.yml b/third_party/cheap-ruler-cpp-2.5.4/.travis.yml new file mode 100644 index 00000000000..260d944a36d --- /dev/null +++ b/third_party/cheap-ruler-cpp-2.5.4/.travis.yml @@ -0,0 +1,25 @@ +language: generic + +matrix: + include: + - os: linux + env: CXX=g++-4.9 + sudo: required + dist: trusty + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'g++-4.9', 'cmake', 'cmake-data' ] + - os: linux + env: CXX=g++-5 + sudo: required + dist: trusty + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'g++-5', 'cmake', 'cmake-data' ] + +cache: apt + +script: + - cmake . && make && ./cheap_ruler diff --git a/third_party/cheap-ruler-cpp-2.5.4/CMakeLists.txt b/third_party/cheap-ruler-cpp-2.5.4/CMakeLists.txt new file mode 100644 index 00000000000..31bc80592da --- /dev/null +++ b/third_party/cheap-ruler-cpp-2.5.4/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) +project(cheap_ruler LANGUAGES CXX C) + +include(cmake/build.cmake) +include(cmake/mason.cmake) + +set(CMAKE_OSX_DEPLOYMENT_TARGET 10.10) +set(CMAKE_CONFIGURATION_TYPES Debug Release) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wextra -Wpedantic -Wshadow") + +mason_use(geometry VERSION 0.9.2 HEADER_ONLY) +mason_use(gtest VERSION 1.8.0) +mason_use(variant VERSION 1.1.4 HEADER_ONLY) + +add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + +add_executable(cheap_ruler + ${PROJECT_SOURCE_DIR}/test/cheap_ruler.cpp +) + +target_include_directories(cheap_ruler + PUBLIC ${PROJECT_SOURCE_DIR}/include +) + +target_add_mason_package(cheap_ruler PRIVATE geometry) +target_add_mason_package(cheap_ruler PRIVATE gtest) +target_add_mason_package(cheap_ruler PRIVATE variant) diff --git a/third_party/cheap-ruler-cpp-2.5.4/LICENSE b/third_party/cheap-ruler-cpp-2.5.4/LICENSE new file mode 100644 index 00000000000..6f4f868bbc9 --- /dev/null +++ b/third_party/cheap-ruler-cpp-2.5.4/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2017, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/third_party/cheap-ruler-cpp-2.5.4/README.md b/third_party/cheap-ruler-cpp-2.5.4/README.md new file mode 100644 index 00000000000..40ceab3e40d --- /dev/null +++ b/third_party/cheap-ruler-cpp-2.5.4/README.md @@ -0,0 +1,199 @@ +# cheap-ruler-cpp + +Port to C++ of [Cheap Ruler](https://github.com/mapbox/cheap-ruler), a collection of very fast approximations to common geodesic measurements. + +[![Build Status](https://travis-ci.org/mapbox/cheap-ruler-cpp.svg?branch=master)](https://travis-ci.org/mapbox/cheap-ruler-cpp) + +# Usage + +```cpp +#include + +namespace cr = mapbox::cheap_ruler; +``` + +All `point`, `line_string`, `polygon`, and `box` references are [mapbox::geometry](https://github.com/mapbox/geometry.hpp) data structures. + +## Create a ruler object + +#### `CheapRuler(double latitude, Unit unit)` + +Creates a ruler object that will approximate measurements around the given latitude with an optional distance unit. Once created, the ruler object has access to the [methods](#methods) below. + +```cpp +auto ruler = cr::CheapRuler(32.8351); +auto milesRuler = cr::CheapRuler(32.8351, cr::CheapRuler::Miles); +``` + +Possible units: + +* `cheap_ruler::CheapRuler::Unit` +* `cheap_ruler::CheapRuler::Kilometers` +* `cheap_ruler::CheapRuler::Miles` +* `cheap_ruler::CheapRuler::NauticalMiles` +* `cheap_ruler::CheapRuler::Meters` +* `cheap_ruler::CheapRuler::Yards` +* `cheap_ruler::CheapRuler::Feet` +* `cheap_ruler::CheapRuler::Inches` + +#### `CheapRuler::fromTile(uint32_t y, uint32_t z)` + +Creates a ruler object from tile coordinates (`y` and `z` integers). Convenient in tile-reduce scripts. + +```cpp +auto ruler = cr::CheapRuler::fromTile(11041, 15); +``` + +## Methods + +#### `distance(point a, point b)` + +Given two points of the form [x = longitude, y = latitude], returns the distance (`double`). + +```cpp +cr::point point_a{-96.9148, 32.8351}; +cr::point point_b{-96.9146, 32.8386}; +auto distance = ruler.distance(point_a, point_b); +std::clog << distance; // 0.388595 +``` + +#### `bearing(point a, point b)` + +Returns the bearing (`double`) between two points in angles. + +```cpp +cr::point point_a{-96.9148, 32.8351}; +cr::point point_b{-96.9146, 32.8386}; +auto bearing = ruler.bearing(point_a, point_b); +std::clog << bearing; // 2.76206 +``` + +#### `destination(point origin, double distance, double bearing)` + +Returns a new point (`point`) given distance and bearing from the starting point. + +```cpp +cr::point point_a{-96.9148, 32.8351}; +auto dest = ruler.destination(point_a, 1.0, -175); +std::clog << dest.x << ", " << dest.y; // -96.9148, 32.8261 +``` + +#### `offset(point origin, double dx, double dy)` + +Returns a new point (`point`) given easting and northing offsets from the starting point. + +```cpp +cr::point point_a{-96.9148, 32.8351}; +auto os = ruler.offset(point_a, 10.0, -5.0); +std::clog << os.x << ", " << os.y; // -96.808, 32.79 +``` + +#### `lineDistance(const line_string& points)` + +Given a line (an array of points), returns the total line distance (`double`). + +```cpp +cr::line_string line_a{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }}; +auto line_distance = ruler.lineDistance(line_a); +std::clog << line_distance; // 88.2962 +``` + +#### `area(polygon poly)` + +Given a polygon (an array of rings, where each ring is an array of points), returns the area (`double`). + +```cpp +cr::linear_ring ring{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }, { -96.9, 32.8 }}; +auto area = ruler.area(cr::polygon{ ring }); +std::clog << area; // +``` + +#### `along(const line_string& line, double distance)` + +Returns the point (`point`) at a specified distance along the line. + +```cpp +cr::linear_ring ring{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }, { -96.9, 32.8 }}; +auto area = ruler.area(cr::polygon{ ring }); +std::clog << area; // 259.581 +``` + +#### `pointOnLine(const line_string& line, point p)` + +Returns a tuple of the form `std::pair` where point is closest point on the line from the given point, index is the start index of the segment with the closest point, and t is a parameter from 0 to 1 that indicates where the closest point is on that segment. + +```cpp +cr::line_string line{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }}; +cr::point point{-96.9, 32.79}; +auto pol = ruler.pointOnLine(line, point); +auto point = std::get<0>(pol); +std::clog << point.x << ", " << point.y; // -96.9, 32.8 (point) +std::clog << std::get<1>(pol); // 0 (index) +std::clog << std::get<2>(pol); // 0. (t) +``` + +#### `lineSlice(point start, point stop, const line_string& line)` + +Returns a part of the given line (`line_string`) between the start and the stop points (or their closest points on the line). + +```cpp +cr::line_string line{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }}; +cr::point start_point{-96.9, 32.8}; +cr::point stop_point{-96.8, 32.8}; +auto slice = ruler.lineSlice(start_point, stop_point, line); +std::clog << slice[0].x << ", " << slice[0].y; // -96.9, 32.8 +std::clog << slice[1].x << ", " << slice[1].y; // -96.8, 32.8 +``` + +#### `lineSliceAlong(double start, double stop, const line_string& line)` + +Returns a part of the given line (`line_string`) between the start and the stop points indicated by distance along the line. + +```cpp +cr::line_string line{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }}; +auto slice = ruler.lineSliceAlong(0.1, 1.2, line); +``` + +#### `bufferPoint(point p, double buffer)` + +Given a point, returns a bounding box object ([w, s, e, n]) created from the given point buffered by a given distance. + +```cpp +cr::point point{-96.9, 32.8}; +auto box = ruler.bufferPoint(point, 0.1); +``` + +#### `bufferBBox(box bbox, double buffer)` + +Given a bounding box, returns the box buffered by a given distance. + +```cpp +cr::box bbox({ 30, 38 }, { 40, 39 }); +auto bbox2 = ruler.bufferBBox(bbox, 1); +``` + +#### `insideBBox(point p, box bbox)` + +Returns true (`bool`) if the given point is inside in the given bounding box, otherwise false. + +```cpp +cr::box bbox({ 30, 38 }, { 40, 39 }); +auto inside = ruler.insideBBox({ 35, 38.5 }, bbox); +std::clog << inside; // true +``` + +# Develop + +```shell +# create targets +cmake . + +# build +make + +# test +./cheap_ruler + +# or just do it all in one! +cmake . && make && ./cheap_ruler +``` diff --git a/third_party/cheap-ruler-cpp-2.5.4/cmake/build.cmake b/third_party/cheap-ruler-cpp-2.5.4/cmake/build.cmake new file mode 100644 index 00000000000..79168336d6d --- /dev/null +++ b/third_party/cheap-ruler-cpp-2.5.4/cmake/build.cmake @@ -0,0 +1,11 @@ +# Generate source groups so the files are properly sorted in IDEs like Xcode. +function(create_source_groups target) + get_target_property(sources ${target} SOURCES) + foreach(file ${sources}) + get_filename_component(file "${file}" ABSOLUTE) + string(REGEX REPLACE "^${CMAKE_SOURCE_DIR}/" "" group "${file}") + get_filename_component(group "${group}" DIRECTORY) + string(REPLACE "/" "\\" group "${group}") + source_group("${group}" FILES "${file}") + endforeach() +endfunction() diff --git a/third_party/cheap-ruler-cpp-2.5.4/cmake/mason.cmake b/third_party/cheap-ruler-cpp-2.5.4/cmake/mason.cmake new file mode 100644 index 00000000000..bc31feeb5f2 --- /dev/null +++ b/third_party/cheap-ruler-cpp-2.5.4/cmake/mason.cmake @@ -0,0 +1,235 @@ +# Mason CMake + +include(CMakeParseArguments) + +function(mason_detect_platform) + # Determine platform + if(NOT MASON_PLATFORM) + # we call uname -s manually here since + # CMAKE_HOST_SYSTEM_NAME will not be defined before the project() call + execute_process( + COMMAND uname -s + OUTPUT_VARIABLE UNAME + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if (UNAME STREQUAL "Darwin") + set(MASON_PLATFORM "osx" PARENT_SCOPE) + else() + set(MASON_PLATFORM "linux" PARENT_SCOPE) + endif() + endif() + + # Determine platform version string + if(NOT MASON_PLATFORM_VERSION) + # Android Studio only passes ANDROID_ABI, but we need to adjust that to the Mason + if(MASON_PLATFORM STREQUAL "android" AND NOT MASON_PLATFORM_VERSION) + if (ANDROID_ABI STREQUAL "armeabi") + set(MASON_PLATFORM_VERSION "arm-v5-9" PARENT_SCOPE) + elseif (ANDROID_ABI STREQUAL "armeabi-v7a") + set(MASON_PLATFORM_VERSION "arm-v7-9" PARENT_SCOPE) + elseif (ANDROID_ABI STREQUAL "arm64-v8a") + set(MASON_PLATFORM_VERSION "arm-v8-21" PARENT_SCOPE) + elseif (ANDROID_ABI STREQUAL "x86") + set(MASON_PLATFORM_VERSION "x86-9" PARENT_SCOPE) + elseif (ANDROID_ABI STREQUAL "x86_64") + set(MASON_PLATFORM_VERSION "x86-64-21" PARENT_SCOPE) + elseif (ANDROID_ABI STREQUAL "mips") + set(MASON_PLATFORM_VERSION "mips-9" PARENT_SCOPE) + elseif (ANDROID_ABI STREQUAL "mips64") + set(MASON_PLATFORM_VERSION "mips-64-9" PARENT_SCOPE) + else() + message(FATAL_ERROR "Unknown ANDROID_ABI '${ANDROID_ABI}'.") + endif() + elseif(MASON_PLATFORM STREQUAL "ios") + set(MASON_PLATFORM_VERSION "8.0" PARENT_SCOPE) + else() + execute_process( + COMMAND uname -m + OUTPUT_VARIABLE MASON_PLATFORM_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(MASON_PLATFORM_VERSION "${MASON_PLATFORM_VERSION}" PARENT_SCOPE) + endif() + endif() +endfunction() + +function(mason_use _PACKAGE) + if(NOT _PACKAGE) + message(FATAL_ERROR "[Mason] No package name given") + endif() + + cmake_parse_arguments("" "HEADER_ONLY" "VERSION" "" ${ARGN}) + + if(_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "[Mason] mason_use() called with unrecognized arguments: ${_UNPARSED_ARGUMENTS}") + endif() + + if(NOT _VERSION) + message(FATAL_ERROR "[Mason] Specifying a version is required") + endif() + + if(MASON_PACKAGE_${_PACKAGE}_INVOCATION STREQUAL "${MASON_INVOCATION}") + # Check that the previous invocation of mason_use didn't select another version of this package + if(NOT MASON_PACKAGE_${_PACKAGE}_VERSION STREQUAL ${_VERSION}) + message(FATAL_ERROR "[Mason] Already using ${_PACKAGE} ${MASON_PACKAGE_${_PACKAGE}_VERSION}. Cannot select version ${_VERSION}.") + endif() + else() + if(_HEADER_ONLY) + set(_PLATFORM_ID "headers") + else() + set(_PLATFORM_ID "${MASON_PLATFORM}-${MASON_PLATFORM_VERSION}") + endif() + + set(_SLUG "${_PLATFORM_ID}/${_PACKAGE}/${_VERSION}") + set(_INSTALL_PATH "${MASON_PACKAGE_DIR}/${_SLUG}") + file(RELATIVE_PATH _INSTALL_PATH_RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${_INSTALL_PATH}") + + if(NOT EXISTS "${_INSTALL_PATH}") + set(_CACHE_PATH "${MASON_PACKAGE_DIR}/.binaries/${_SLUG}.tar.gz") + if (NOT EXISTS "${_CACHE_PATH}") + # Download the package + set(_URL "${MASON_REPOSITORY}/${_SLUG}.tar.gz") + message("[Mason] Downloading package ${_URL}...") + + set(_FAILED) + set(_ERROR) + # Note: some CMake versions are compiled without SSL support + get_filename_component(_CACHE_DIR "${_CACHE_PATH}" DIRECTORY) + file(MAKE_DIRECTORY "${_CACHE_DIR}") + execute_process( + COMMAND curl --retry 3 -s -f -S -L "${_URL}" -o "${_CACHE_PATH}.tmp" + RESULT_VARIABLE _FAILED + ERROR_VARIABLE _ERROR) + if(_FAILED) + message(FATAL_ERROR "[Mason] Failed to download ${_URL}: ${_ERROR}") + else() + # We downloaded to a temporary file to prevent half-finished downloads + file(RENAME "${_CACHE_PATH}.tmp" "${_CACHE_PATH}") + endif() + endif() + + # Unpack the package + message("[Mason] Unpacking package to ${_INSTALL_PATH_RELATIVE}...") + file(MAKE_DIRECTORY "${_INSTALL_PATH}") + execute_process( + COMMAND ${CMAKE_COMMAND} -E tar xzf "${_CACHE_PATH}" + WORKING_DIRECTORY "${_INSTALL_PATH}") + endif() + + # Error out if there is no config file. + if(NOT EXISTS "${_INSTALL_PATH}/mason.ini") + message(FATAL_ERROR "[Mason] Could not find mason.ini for package ${_PACKAGE} ${_VERSION}") + endif() + + set(MASON_PACKAGE_${_PACKAGE}_PREFIX "${_INSTALL_PATH}" CACHE STRING "${_PACKAGE} ${_INSTALL_PATH}" FORCE) + mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_PREFIX) + + # Load the configuration from the ini file + file(STRINGS "${_INSTALL_PATH}/mason.ini" _CONFIG_FILE) + foreach(_LINE IN LISTS _CONFIG_FILE) + string(REGEX MATCH "^([a-z_]+) *= *" _KEY "${_LINE}") + if (_KEY) + string(LENGTH "${_KEY}" _KEY_LENGTH) + string(SUBSTRING "${_LINE}" ${_KEY_LENGTH} -1 _VALUE) + string(REGEX REPLACE ";.*$" "" _VALUE "${_VALUE}") # Trim trailing commas + string(REPLACE "{prefix}" "${_INSTALL_PATH}" _VALUE "${_VALUE}") + string(STRIP "${_VALUE}" _VALUE) + string(REPLACE "=" "" _KEY "${_KEY}") + string(STRIP "${_KEY}" _KEY) + string(TOUPPER "${_KEY}" _KEY) + if(_KEY STREQUAL "INCLUDE_DIRS" OR _KEY STREQUAL "STATIC_LIBS" ) + separate_arguments(_VALUE) + endif() + set(MASON_PACKAGE_${_PACKAGE}_${_KEY} "${_VALUE}" CACHE STRING "${_PACKAGE} ${_KEY}" FORCE) + mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_${_KEY}) + endif() + endforeach() + + # Compare version in the package to catch errors early on + if(NOT _VERSION STREQUAL MASON_PACKAGE_${_PACKAGE}_VERSION) + message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has version '${MASON_PACKAGE_${_PACKAGE}_VERSION}', but required '${_VERSION}'") + endif() + + if(NOT _PACKAGE STREQUAL MASON_PACKAGE_${_PACKAGE}_NAME) + message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has name '${MASON_PACKAGE_${_PACKAGE}_NAME}', but required '${_NAME}'") + endif() + + if(NOT _HEADER_ONLY) + if(NOT MASON_PLATFORM STREQUAL MASON_PACKAGE_${_PACKAGE}_PLATFORM) + message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has platform '${MASON_PACKAGE_${_PACKAGE}_PLATFORM}', but required '${MASON_PLATFORM}'") + endif() + + if(NOT MASON_PLATFORM_VERSION STREQUAL MASON_PACKAGE_${_PACKAGE}_PLATFORM_VERSION) + message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has platform version '${MASON_PACKAGE_${_PACKAGE}_PLATFORM_VERSION}', but required '${MASON_PLATFORM_VERSION}'") + endif() + endif() + + # Concatenate the static libs and libraries + set(_LIBRARIES) + list(APPEND _LIBRARIES ${MASON_PACKAGE_${_PACKAGE}_STATIC_LIBS} ${MASON_PACKAGE_${_PACKAGE}_LDFLAGS}) + set(MASON_PACKAGE_${_PACKAGE}_LIBRARIES "${_LIBRARIES}" CACHE STRING "${_PACKAGE} _LIBRARIES" FORCE) + mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_LIBRARIES) + + if(NOT _HEADER_ONLY) + string(REGEX MATCHALL "(^| +)-L *([^ ]+)" MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LDFLAGS}") + string(REGEX REPLACE "(^| +)-L *" "\\1" MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}") + set(MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}" CACHE STRING "${_PACKAGE} ${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}" FORCE) + mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS) + endif() + + # Store invocation ID to prevent different versions of the same package in one invocation + set(MASON_PACKAGE_${_PACKAGE}_INVOCATION "${MASON_INVOCATION}" CACHE INTERNAL "${_PACKAGE} invocation ID" FORCE) + endif() +endfunction() + +macro(target_add_mason_package _TARGET _VISIBILITY _PACKAGE) + if (NOT MASON_PACKAGE_${_PACKAGE}_INVOCATION) + message(FATAL_ERROR "[Mason] Package ${_PACKAGE} has not been initialized yet") + endif() + + target_include_directories(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_INCLUDE_DIRS}") + target_compile_definitions(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_DEFINITIONS}") + target_compile_options(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_OPTIONS}") + target_link_libraries(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_LIBRARIES}") +endmacro() + +# Setup + +string(RANDOM LENGTH 16 MASON_INVOCATION) + +# Read environment variables if CMake is run in command mode +if (CMAKE_ARGC) + set(MASON_PLATFORM "$ENV{MASON_PLATFORM}") + set(MASON_PLATFORM_VERSION "$ENV{MASON_PLATFORM_VERSION}") + set(MASON_PACKAGE_DIR "$ENV{MASON_PACKAGE_DIR}") + set(MASON_REPOSITORY "$ENV{MASON_REPOSITORY}") +endif() + +# Directory where Mason packages are located; typically ends with mason_packages +if (NOT MASON_PACKAGE_DIR) + set(MASON_PACKAGE_DIR "${CMAKE_SOURCE_DIR}/mason_packages") +endif() + +# URL prefix of where packages are located. +if (NOT MASON_REPOSITORY) + set(MASON_REPOSITORY "https://mason-binaries.s3.amazonaws.com") +endif() + +mason_detect_platform() + +# Execute commands if CMake is run in command mode +if (CMAKE_ARGC) + # Collect remaining arguments for passing to mason_use + set(_MASON_ARGS) + foreach(I RANGE 4 ${CMAKE_ARGC}) + list(APPEND _MASON_ARGS "${CMAKE_ARGV${I}}") + endforeach() + + # Install the package + mason_use(${_MASON_ARGS}) + + # Optionally print variables + if(DEFINED MASON_PACKAGE_${CMAKE_ARGV4}_${CMAKE_ARGV3}) + # CMake can't write to stdout with message() + execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${MASON_PACKAGE_${CMAKE_ARGV4}_${CMAKE_ARGV3}}") + endif() +endif() diff --git a/third_party/cheap-ruler-cpp-2.5.4/include/mapbox/cheap_ruler.hpp b/third_party/cheap-ruler-cpp-2.5.4/include/mapbox/cheap_ruler.hpp new file mode 100644 index 00000000000..d5ae7428b28 --- /dev/null +++ b/third_party/cheap-ruler-cpp-2.5.4/include/mapbox/cheap_ruler.hpp @@ -0,0 +1,354 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace mapbox { +namespace cheap_ruler { + +using box = geometry::box; +using line_string = geometry::line_string; +using linear_ring = geometry::linear_ring; +using multi_line_string = geometry::multi_line_string; +using point = geometry::point; +using polygon = geometry::polygon; + +class CheapRuler { +public: + enum Unit { + Kilometers, + Miles, + NauticalMiles, + Meters, + Metres = Meters, + Yards, + Feet, + Inches + }; + + // + // A collection of very fast approximations to common geodesic measurements. Useful + // for performance-sensitive code that measures things on a city scale. Point coordinates + // are in the [x = longitude, y = latitude] form. + // + explicit CheapRuler(double latitude, Unit unit = Kilometers) { + double m = 0.; + + switch (unit) { + case Kilometers: + m = 1.; + break; + case Miles: + m = 1000. / 1609.344; + break; + case NauticalMiles: + m = 1000. / 1852.; + break; + case Meters: + m = 1000.; + break; + case Yards: + m = 1000. / 0.9144; + break; + case Feet: + m = 1000. / 0.3048; + break; + case Inches: + m = 1000. / 0.0254; + break; + } + + auto cos = std::cos(latitude * M_PI / 180.); + auto cos2 = 2. * cos * cos - 1.; + auto cos3 = 2. * cos * cos2 - cos; + auto cos4 = 2. * cos * cos3 - cos2; + auto cos5 = 2. * cos * cos4 - cos3; + + // multipliers for converting longitude and latitude + // degrees into distance (http://1.usa.gov/1Wb1bv7) + kx = m * (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5); + ky = m * (111.13209 - 0.56605 * cos2 + 0.0012 * cos4); + } + + static CheapRuler fromTile(uint32_t y, uint32_t z) { + double n = M_PI * (1. - 2. * (y + 0.5) / std::pow(2., z)); + double latitude = std::atan(0.5 * (std::exp(n) - std::exp(-n))) * 180. / M_PI; + + return CheapRuler(latitude); + } + + // + // Given two points of the form [x = longitude, y = latitude], returns the distance. + // + double distance(point a, point b) { + auto dx = (a.x - b.x) * kx; + auto dy = (a.y - b.y) * ky; + + return std::sqrt(dx * dx + dy * dy); + } + + // + // Returns the bearing between two points in angles. + // + double bearing(point a, point b) { + auto dx = (b.x - a.x) * kx; + auto dy = (b.y - a.y) * ky; + + if (!dx && !dy) { + return 0.; + } + + auto value = std::atan2(dx, dy) * 180. / M_PI; + + if (value > 180.) { + value -= 360.; + } + + return value; + } + + // + // Returns a new point given distance and bearing from the starting point. + // + point destination(point origin, double dist, double bearing_) { + auto a = (90. - bearing_) * M_PI / 180.; + + return offset(origin, std::cos(a) * dist, std::sin(a) * dist); + } + + // + // Returns a new point given easting and northing offsets from the starting point. + // + point offset(point origin, double dx, double dy) { + return point(origin.x + dx / kx, origin.y + dy / ky); + } + + // + // Given a line (an array of points), returns the total line distance. + // + double lineDistance(const line_string& points) { + double total = 0.; + + for (unsigned i = 0; i < points.size() - 1; ++i) { + total += distance(points[i], points[i + 1]); + } + + return total; + } + + // + // Given a polygon (an array of rings, where each ring is an array of points), + // returns the area. + // + double area(polygon poly) { + double sum = 0.; + + for (unsigned i = 0; i < poly.size(); ++i) { + auto& ring = poly[i]; + + for (unsigned j = 0, len = ring.size(), k = len - 1; j < len; k = j++) { + sum += (ring[j].x - ring[k].x) * (ring[j].y + ring[k].y) * (i ? -1. : 1.); + } + } + + return (std::abs(sum) / 2.) * kx * ky; + } + + // + // Returns the point at a specified distance along the line. + // + point along(const line_string& line, double dist) { + double sum = 0.; + + if (dist <= 0.) { + return line[0]; + } + + for (unsigned i = 0; i < line.size() - 1; ++i) { + auto p0 = line[i]; + auto p1 = line[i + 1]; + auto d = distance(p0, p1); + + sum += d; + + if (sum > dist) { + return interpolate(p0, p1, (dist - (sum - d)) / d); + } + } + + return line[line.size() - 1]; + } + + // + // Returns a tuple of the form where point is closest point on the line + // from the given point, index is the start index of the segment with the closest point, + // and t is a parameter from 0 to 1 that indicates where the closest point is on that segment. + // + std::tuple pointOnLine(const line_string& line, point p) { + double minDist = std::numeric_limits::infinity(); + double minX = 0., minY = 0., minI = 0., minT = 0.; + + for (unsigned i = 0; i < line.size() - 1; ++i) { + auto t = 0.; + auto x = line[i].x; + auto y = line[i].y; + auto dx = (line[i + 1].x - x) * kx; + auto dy = (line[i + 1].y - y) * ky; + + if (dx != 0. || dy != 0.) { + t = ((p.x - x) * kx * dx + (p.y - y) * ky * dy) / (dx * dx + dy * dy); + + if (t > 1) { + x = line[i + 1].x; + y = line[i + 1].y; + + } else if (t > 0) { + x += (dx / kx) * t; + y += (dy / ky) * t; + } + } + + dx = (p.x - x) * kx; + dy = (p.y - y) * ky; + + auto sqDist = dx * dx + dy * dy; + + if (sqDist < minDist) { + minDist = sqDist; + minX = x; + minY = y; + minI = i; + minT = t; + } + } + + return std::make_tuple( + point(minX, minY), minI, ::fmax(0., ::fmin(1., minT))); + } + + // + // Returns a part of the given line between the start and the stop points (or their closest + // points on the line). + // + line_string lineSlice(point start, point stop, const line_string& line) { + auto getPoint = [](auto tuple) { return std::get<0>(tuple); }; + auto getIndex = [](auto tuple) { return std::get<1>(tuple); }; + auto getT = [](auto tuple) { return std::get<2>(tuple); }; + + auto p1 = pointOnLine(line, start); + auto p2 = pointOnLine(line, stop); + + if (getIndex(p1) > getIndex(p2) || (getIndex(p1) == getIndex(p2) && getT(p1) > getT(p2))) { + auto tmp = p1; + p1 = p2; + p2 = tmp; + } + + line_string slice = { getPoint(p1) }; + + auto l = getIndex(p1) + 1; + auto r = getIndex(p2); + + if (line[l] != slice[0] && l <= r) { + slice.push_back(line[l]); + } + + for (unsigned i = l + 1; i <= r; ++i) { + slice.push_back(line[i]); + } + + if (line[r] != getPoint(p2)) { + slice.push_back(getPoint(p2)); + } + + return slice; + }; + + // + // Returns a part of the given line between the start and the stop points + // indicated by distance along the line. + // + line_string lineSliceAlong(double start, double stop, const line_string& line) { + double sum = 0.; + line_string slice; + + for (unsigned i = 0; i < line.size() - 1; ++i) { + auto p0 = line[i]; + auto p1 = line[i + 1]; + auto d = distance(p0, p1); + + sum += d; + + if (sum > start && slice.size() == 0) { + slice.push_back(interpolate(p0, p1, (start - (sum - d)) / d)); + } + + if (sum >= stop) { + slice.push_back(interpolate(p0, p1, (stop - (sum - d)) / d)); + return slice; + } + + if (sum > start) { + slice.push_back(p1); + } + } + + return slice; + }; + + // + // Given a point, returns a bounding box object ([w, s, e, n]) + // created from the given point buffered by a given distance. + // + box bufferPoint(point p, double buffer) { + auto v = buffer / ky; + auto h = buffer / kx; + + return box( + point(p.x - h, p.y - v), + point(p.x + h, p.y + v) + ); + } + + // + // Given a bounding box, returns the box buffered by a given distance. + // + box bufferBBox(box bbox, double buffer) { + auto v = buffer / ky; + auto h = buffer / kx; + + return box( + point(bbox.min.x - h, bbox.min.y - v), + point(bbox.max.x + h, bbox.max.y + v) + ); + } + + // + // Returns true if the given point is inside in the given bounding box, otherwise false. + // + bool insideBBox(point p, box bbox) { + return p.x >= bbox.min.x && + p.x <= bbox.max.x && + p.y >= bbox.min.y && + p.y <= bbox.max.y; + } + + static point interpolate(point a, point b, double t) { + double dx = b.x - a.x; + double dy = b.y - a.y; + + return point(a.x + dx * t, a.y + dy * t); + } + +private: + double ky; + double kx; +}; + +} // namespace cheap_ruler +} // namespace mapbox diff --git a/third_party/cheap-ruler-cpp-2.5.4/test/cheap_ruler.cpp b/third_party/cheap-ruler-cpp-2.5.4/test/cheap_ruler.cpp new file mode 100644 index 00000000000..0ac73c81fb0 --- /dev/null +++ b/third_party/cheap-ruler-cpp-2.5.4/test/cheap_ruler.cpp @@ -0,0 +1,200 @@ +#include +#include + +#include "fixtures/lines.hpp" +#include "fixtures/turf.hpp" + +namespace cr = mapbox::cheap_ruler; + +class CheapRulerTest : public ::testing::Test { +protected: + cr::CheapRuler ruler = cr::CheapRuler(32.8351); + cr::CheapRuler milesRuler = cr::CheapRuler(32.8351, cr::CheapRuler::Miles); +}; + +void assertErr(double expected, double actual, double maxError) { + // Add a negligible fraction to make sure we + // don't divide by zero. + double error = std::abs((actual - expected) / + (expected == 0. ? expected + 0.000001 : expected)); + + if (error > maxError) { + FAIL() << "expected is " << expected << " but got " << actual; + } +} + +TEST_F(CheapRulerTest, distance) { + for (unsigned i = 0; i < points.size() - 1; ++i) { + auto expected = turf_distance[i]; + auto actual = ruler.distance(points[i], points[i + 1]); + + assertErr(expected, actual, .003); + } +} + +TEST_F(CheapRulerTest, distanceInMiles) { + auto d = ruler.distance({ 30.5, 32.8351 }, { 30.51, 32.8451 }); + auto d2 = milesRuler.distance({ 30.5, 32.8351 }, { 30.51, 32.8451 }); + + assertErr(d / d2, 1.609344, 1e-12); +} + +TEST_F(CheapRulerTest, bearing) { + for (unsigned i = 0; i < points.size() - 1; ++i) { + auto expected = turf_bearing[i]; + auto actual = ruler.bearing(points[i], points[i + 1]); + + assertErr(expected, actual, .005); + } +} + +TEST_F(CheapRulerTest, destination) { + for (unsigned i = 0; i < points.size(); ++i) { + auto bearing = (i % 360) - 180.; + auto expected = turf_destination[i]; + auto actual = ruler.destination(points[i], 1.0, bearing); + + assertErr(expected.x, actual.x, 1e-6); // longitude + assertErr(expected.y, actual.y, 1e-6); // latitude + } +} + +TEST_F(CheapRulerTest, lineDistance) { + for (unsigned i = 0; i < lines.size(); ++i) { + auto expected = turf_lineDistance[i]; + auto actual = ruler.lineDistance(lines[i]); + + assertErr(expected, actual, 0.003); + } +} + +TEST_F(CheapRulerTest, area) { + for (unsigned i = 0, j = 0; i < lines.size(); ++i) { + if (lines[i].size() < 3) { + continue; + } + + cr::linear_ring ring; + for (auto point : lines[i]) { + ring.push_back(point); + } + ring.push_back(lines[i][0]); + + auto expected = turf_area[j++]; + auto actual = ruler.area(cr::polygon{ ring }); + + assertErr(expected, actual, 0.003); + } +} + +TEST_F(CheapRulerTest, along) { + for (unsigned i = 0; i < lines.size(); ++i) { + auto expected = turf_along[i]; + auto actual = ruler.along(lines[i], turf_along_dist[i]); + + assertErr(expected.x, actual.x, 1e-6); // along longitude + assertErr(expected.y, actual.y, 1e-6); // along latitude + } +} + +TEST_F(CheapRulerTest, alongWithDist) { + ASSERT_EQ(ruler.along(lines[0], -5), lines[0][0]); +} + +TEST_F(CheapRulerTest, alongWithDistGreaterThanLength) { + ASSERT_EQ(ruler.along(lines[0], 1000), lines[0][lines[0].size() - 1]); +} + +TEST_F(CheapRulerTest, pointOnLine) { + // not Turf comparison because pointOnLine is bugged https://github.com/Turfjs/turf/issues/344 + cr::line_string line = {{ -77.031669, 38.878605 }, { -77.029609, 38.881946 }}; + auto result = ruler.pointOnLine(line, { -77.034076, 38.882017 }); + + ASSERT_EQ(std::get<0>(result), cr::point(-77.03052697027461, 38.880457194811896)); // point + ASSERT_EQ(std::get<1>(result), 0u); // index + ASSERT_EQ(std::get<2>(result), 0.5543833618360235); // t + + ASSERT_EQ(std::get<2>(ruler.pointOnLine(line, { -80., 38. })), 0.) << "t is not less than 0"; + ASSERT_EQ(std::get<2>(ruler.pointOnLine(line, { -75., 38. })), 1.) << "t is not bigger than 1"; +} + +TEST_F(CheapRulerTest, lineSlice) { + for (unsigned i = 0; i < lines.size(); ++i) { + auto line = lines[i]; + auto dist = ruler.lineDistance(line); + auto start = ruler.along(line, dist * 0.3); + auto stop = ruler.along(line, dist * 0.7); + auto expected = turf_lineSlice[i]; + auto actual = ruler.lineDistance(ruler.lineSlice(start, stop, line)); + + assertErr(expected, actual, 1e-5); + } +} + +TEST_F(CheapRulerTest, lineSliceAlong) { + for (unsigned i = 0; i < lines.size(); ++i) { + if (i == 46) { + // skip due to Turf bug https://github.com/Turfjs/turf/issues/351 + continue; + }; + + auto line = lines[i]; + auto dist = ruler.lineDistance(line); + auto expected = turf_lineSlice[i]; + auto actual = ruler.lineDistance(ruler.lineSliceAlong(dist * 0.3, dist * 0.7, line)); + + assertErr(expected, actual, 1e-5); + } +} + +TEST_F(CheapRulerTest, lineSliceReverse) { + auto line = lines[0]; + auto dist = ruler.lineDistance(line); + auto start = ruler.along(line, dist * 0.7); + auto stop = ruler.along(line, dist * 0.3); + auto actual = ruler.lineDistance(ruler.lineSlice(start, stop, line)); + + ASSERT_EQ(actual, 0.018676802802910702); +} + +TEST_F(CheapRulerTest, bufferPoint) { + for (unsigned i = 0; i < points.size(); ++i) { + auto expected = turf_bufferPoint[i]; + auto actual = milesRuler.bufferPoint(points[i], 0.1); + + assertErr(expected.min.x, actual.min.x, 2e-7); + assertErr(expected.min.x, actual.min.x, 2e-7); + assertErr(expected.max.y, actual.max.y, 2e-7); + assertErr(expected.max.y, actual.max.y, 2e-7); + } +} + +TEST_F(CheapRulerTest, bufferBBox) { + cr::box bbox({ 30, 38 }, { 40, 39 }); + cr::box bbox2 = ruler.bufferBBox(bbox, 1); + + ASSERT_EQ(bbox2, cr::box({ 29.989319515875376, 37.99098271225711 }, { 40.01068048412462, 39.00901728774289 })); +} + +TEST_F(CheapRulerTest, insideBBox) { + cr::box bbox({ 30, 38 }, { 40, 39 }); + + ASSERT_TRUE(ruler.insideBBox({ 35, 38.5 }, bbox)); + ASSERT_FALSE(ruler.insideBBox({ 45, 45 }, bbox)); +} + +TEST_F(CheapRulerTest, fromTile) { + auto ruler1 = cr::CheapRuler(50.5); + auto ruler2 = cr::CheapRuler::fromTile(11041, 15); + + cr::point p1(30.5, 50.5); + cr::point p2(30.51, 50.51); + + assertErr(ruler1.distance(p1, p2), ruler2.distance(p1, p2), 2e-5); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/third_party/cheap-ruler-cpp-2.5.4/test/fixtures/lines.hpp b/third_party/cheap-ruler-cpp-2.5.4/test/fixtures/lines.hpp new file mode 100644 index 00000000000..d2ec79d3435 --- /dev/null +++ b/third_party/cheap-ruler-cpp-2.5.4/test/fixtures/lines.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace cr = mapbox::cheap_ruler; + +static const cr::multi_line_string lines = {{{ -96.920341, 32.838261 }, { -96.920421, 32.838295 }, { -96.920421, 32.838295 }, { -96.920536, 32.838297 }, { -96.920684, 32.838293 }, { -96.920818, 32.838342 }}, {{ -96.920349, 32.838306 }, { -96.920421, 32.838295 }}, {{ -96.919874, 32.837479 }, { -96.920097, 32.837684 }, { -96.92018, 32.837844 }, { -96.92029, 32.838216 }, { -96.920341, 32.838261 }}, {{ -96.915781, 32.834689 }, { -96.915735, 32.834529 }, { -96.91573, 32.834443 }, { -96.915733, 32.834286 }, { -96.915853, 32.8337 }}, {{ -96.915092, 32.836889 }, { -96.915915, 32.836977 }}, {{ -96.914617, 32.83752 }, { -96.914507, 32.838106 }, { -96.914319, 32.839063 }}, {{ -96.915671, 32.833443 }, { -96.915427, 32.833817 }, { -96.91532, 32.833963 }, { -96.915247, 32.834072 }, { -96.915062, 32.834252 }, { -96.914775, 32.834518 }}, {{ -96.9104, 32.837119 }, { -96.910567, 32.837069 }, { -96.9111, 32.836941 }}, {{ -96.91441, 32.836582 }, { -96.914225, 32.836544 }, { -96.914158, 32.836499 }, { -96.914107, 32.836449 }, { -96.914075, 32.836393 }, { -96.914059, 32.836292 }, { -96.914091, 32.836073 }, { -96.914649, 32.834919 }, { -96.914837, 32.834601 }, { -96.914858, 32.834583 }, { -96.914912, 32.834563 }, { -96.914968, 32.834561 }, { -96.91503, 32.83457 }, { -96.915081, 32.834592 }, { -96.915116, 32.834628 }, { -96.915134, 32.834671 }, { -96.91514, 32.834718 }, { -96.915124, 32.834793 }, { -96.914931, 32.835336 }, { -96.914888, 32.835509 }, { -96.91481, 32.835911 }}, {{ -96.914155, 32.836758 }, { -96.913391, 32.836697 }, { -96.912991, 32.836643 }, { -96.912847, 32.836589 }, { -96.912761, 32.836508 }, { -96.912573, 32.836433 }}, {{ -96.917377, 32.837281 }, { -96.917589, 32.837317 }, { -96.918117, 32.837339 }}, {{ -96.920815, 32.836594 }, { -96.919732, 32.836643 }, { -96.918975, 32.836702 }, { -96.918077, 32.836729 }, { -96.916661, 32.83672 }, { -96.916358, 32.836774 }}, {{ -96.911543, 32.836925 }, { -96.911806, 32.837006 }, { -96.911994, 32.837028 }, { -96.912326, 32.837114 }, { -96.912552, 32.837202 }, { -96.912656, 32.83729 }}, {{ -96.914657, 32.836659 }, { -96.914518, 32.837522 }, { -96.914472, 32.837673 }, { -96.914383, 32.837772 }, { -96.914284, 32.837858 }, { -96.914153, 32.837905 }, { -96.914024, 32.837912 }, { -96.913925, 32.837885 }, { -96.913804, 32.837828 }, { -96.913678, 32.837729 }, { -96.913391, 32.837479 }, { -96.913278, 32.837328 }, { -96.913252, 32.837238 }, { -96.913262, 32.837148 }, { -96.913305, 32.837078 }, { -96.91337, 32.837024 }, { -96.91348, 32.837004 }, { -96.914483, 32.83688 }}, {{ -96.915915, 32.836977 }, { -96.916183, 32.837004 }, { -96.916425, 32.837042 }, { -96.916862, 32.837164 }}, {{ -96.915183, 32.840102 }, { -96.915151, 32.839929 }, { -96.915113, 32.839539 }, { -96.915008, 32.838973 }, { -96.914949, 32.83873 }, { -96.914909, 32.838491 }, { -96.914834, 32.838234 }}, {{ -96.914728, 32.842674 }, { -96.914772, 32.84257 }, { -96.914872, 32.842277 }, { -96.914974, 32.841939 }, { -96.915014, 32.841646 }, { -96.915065, 32.84138 }, { -96.915113, 32.84099 }, { -96.915175, 32.840321 }, { -96.915193, 32.839827 }, { -96.915204, 32.839345 }, { -96.91525, 32.839045 }, { -96.915325, 32.838759 }, { -96.915483, 32.838464 }, { -96.915658, 32.838236 }, { -96.915907, 32.837943 }, { -96.91621, 32.837709 }, { -96.91636, 32.837612 }, { -96.916588, 32.837481 }, { -96.916948, 32.837324 }, { -96.91731, 32.837188 }, { -96.918026, 32.837098 }, { -96.918847, 32.83706 }, { -96.919391, 32.83704 }, { -96.919847, 32.83701 }, { -96.920174, 32.836961 }}, {{ -96.914603, 32.839688 }, { -96.914335, 32.841087 }, { -96.914099, 32.841896 }, { -96.913823, 32.842577 }, { -96.91377, 32.842674 }}, {{ -96.915915, 32.836977 }, { -96.916105, 32.837042 }, { -96.916183, 32.837116 }, { -96.916218, 32.837164 }, { -96.916232, 32.83722 }, { -96.916223, 32.837308 }, { -96.916202, 32.837355 }, { -96.916006, 32.837556 }, { -96.915714, 32.837779 }, { -96.915588, 32.83784 }, { -96.915419, 32.837871 }, { -96.915293, 32.837867 }, { -96.91514, 32.837828 }, { -96.915062, 32.837774 }, { -96.915014, 32.837734 }, { -96.914925, 32.837614 }, { -96.914888, 32.837515 }, { -96.914831, 32.837297 }}, {{ -96.917743, 32.833443 }, { -96.917766, 32.836553 }}, {{ -96.912399, 32.842674 }, { -96.911487, 32.841373 }, { -96.9104, 32.841399 }}, {{ -96.91072, 32.84264 }, { -96.910717, 32.842674 }}, {{ -96.911584, 32.833443 }, { -96.911581, 32.833684 }, { -96.911559, 32.835271 }, { -96.911554, 32.83642 }}, {{ -96.920403, 32.833443 }, { -96.920397, 32.833894 }}, {{ -96.9104, 32.833662 }, { -96.911581, 32.833684 }}, {{ -96.914775, 32.834518 }, { -96.914512, 32.835038 }, { -96.914327, 32.835437 }, { -96.914016, 32.836136 }, { -96.913973, 32.83621 }, { -96.913906, 32.83628 }, { -96.913769, 32.836359 }, { -96.913675, 32.836384 }, { -96.913203, 32.836445 }}, {{ -96.9104, 32.837231 }, { -96.910695, 32.837202 }, { -96.911025, 32.837182 }, { -96.911355, 32.837168 }, { -96.911948, 32.837168 }, { -96.912391, 32.837222 }, { -96.912656, 32.83729 }, { -96.913072, 32.83752 }}, {{ -96.916189, 32.83818 }, { -96.918117, 32.837339 }}, {{ -96.918165, 32.84179 }, { -96.917329, 32.840988 }, { -96.91702, 32.840704 }, { -96.916811, 32.840587 }, { -96.916661, 32.840519 }, { -96.916368, 32.840454 }, { -96.916164, 32.840438 }, { -96.915263, 32.840458 }}, {{ -96.914137, 32.840553 }, { -96.914316, 32.840555 }}, {{ -96.91415, 32.840452 }, { -96.914054, 32.841182 }, { -96.913976, 32.841572 }, { -96.913842, 32.842049 }, { -96.913691, 32.842414 }, { -96.913569, 32.842674 }}, {{ -96.914919, 32.842674 }, { -96.91492, 32.842671 }, { -96.9151, 32.842171 }, { -96.915244, 32.84152 }, { -96.915322, 32.840979 }, { -96.915376, 32.840569 }, { -96.915384, 32.840454 }}, {{ -96.915474, 32.833443 }, { -96.915379, 32.833664 }, { -96.915314, 32.833772 }, { -96.915261, 32.833849 }, { -96.915081, 32.834083 }, { -96.914775, 32.834518 }}, {{ -96.913072, 32.83752 }, { -96.913195, 32.837608 }, { -96.913675, 32.838002 }, { -96.913694, 32.838011 }, { -96.91396, 32.838241 }, { -96.914054, 32.838345 }, { -96.914161, 32.838507 }, { -96.914255, 32.838714 }, { -96.914292, 32.83889 }, { -96.914319, 32.839063 }, { -96.914182, 32.840098 }, { -96.91415, 32.840452 }}, {{ -96.917406, 32.836557 }, { -96.91588, 32.836517 }, { -96.915778, 32.836501 }, { -96.915727, 32.836481 }, { -96.91569, 32.836458 }, { -96.915644, 32.836422 }, { -96.915609, 32.836382 }, { -96.915588, 32.836348 }, { -96.915572, 32.836305 }, { -96.915564, 32.836235 }, { -96.915564, 32.836154 }, { -96.915668, 32.835469 }, { -96.915754, 32.834827 }, { -96.915781, 32.834689 }, { -96.915835, 32.834556 }, { -96.916052, 32.834074 }, { -96.916283, 32.833443 }}, {{ -96.915384, 32.840454 }, { -96.915408, 32.839794 }, { -96.915411, 32.839345 }, { -96.915459, 32.839068 }, { -96.91554, 32.838845 }, { -96.915636, 32.838687 }, { -96.915765, 32.838529 }, { -96.915945, 32.838358 }, { -96.916189, 32.83818 }}, {{ -96.919421, 32.837159 }, { -96.919557, 32.837216 }, { -96.919697, 32.837288 }, { -96.919812, 32.8374 }, { -96.919874, 32.837479 }, { -96.920172, 32.838358 }, { -96.920295, 32.838845 }, { -96.920333, 32.839068 }, { -96.920327, 32.839422 }, { -96.920223, 32.839746 }, { -96.920072, 32.840046 }, { -96.919898, 32.840276 }, { -96.919812, 32.840355 }, { -96.919426, 32.84067 }, { -96.918012, 32.84172 }, { -96.91753, 32.84207 }, { -96.917374, 32.842196 }, { -96.916727, 32.842674 }}, {{ -96.916981, 32.842674 }, { -96.917079, 32.842602 }, { -96.917487, 32.842291 }, { -96.918114, 32.841826 }, { -96.919984, 32.840454 }, { -96.920236, 32.840233 }, { -96.920429, 32.839965 }, { -96.920512, 32.839816 }, { -96.92059, 32.839557 }, { -96.920612, 32.839271 }, { -96.920604, 32.839181 }, { -96.920574, 32.839043 }, { -96.920416, 32.83857 }, { -96.920365, 32.83839 }, { -96.920341, 32.838261 }, { -96.9203, 32.838119 }, { -96.920282, 32.837943 }, { -96.920274, 32.83775 }, { -96.920295, 32.837603 }, { -96.920319, 32.837536 }, { -96.920397, 32.837407 }, { -96.920515, 32.837288 }, { -96.920601, 32.837227 }, { -96.920727, 32.83717 }}, {{ -96.91441, 32.836582 }, { -96.914188, 32.836571 }, { -96.913203, 32.836445 }}, {{ -96.914324, 32.840454 }, { -96.91415, 32.840452 }}, {{ -96.921387, 32.836595 }, { -96.920815, 32.836594 }, { -96.917766, 32.836553 }, { -96.917406, 32.836557 }, { -96.915851, 32.836621 }, { -96.915459, 32.836621 }, { -96.915129, 32.836607 }}, {{ -96.918117, 32.837339 }, { -96.918324, 32.837252 }, { -96.918586, 32.837186 }, { -96.918844, 32.837161 }, { -96.919421, 32.837159 }, { -96.920727, 32.83717 }, { -96.921387, 32.83717 }}, {{ -96.913203, 32.836445 }, { -96.913142, 32.836438 }, { -96.912999, 32.836438 }, { -96.912774, 32.836427 }, { -96.912573, 32.836433 }, { -96.911554, 32.83642 }, { -96.9104, 32.836416 }}, {{ -96.915253, 32.840564 }, { -96.915376, 32.840569 }, { -96.915816, 32.840562 }, { -96.916197, 32.840567 }, { -96.916478, 32.840598 }, { -96.916623, 32.840641 }, { -96.916757, 32.840702 }, { -96.916913, 32.840794 }, { -96.917326, 32.841157 }, { -96.917452, 32.841272 }, { -96.917956, 32.841761 }, { -96.918061, 32.841867 }}, {{ -96.9145, 32.842674 }, { -96.914547, 32.842534 }, { -96.914708, 32.841971 }, { -96.914802, 32.841473 }, { -96.914858, 32.841078 }, { -96.914882, 32.840742 }, { -96.914907, 32.840224 }, { -96.914915, 32.839706 }, { -96.914885, 32.839084 }, { -96.914826, 32.83811 }, { -96.914831, 32.837297 }}, {{ -96.914987, 32.836886 }, { -96.915092, 32.836889 }}, {{ -96.914526, 32.836763 }, { -96.914155, 32.836758 }, { -96.9104, 32.836758 }}, {{ -96.921387, 32.836813 }, { -96.918825, 32.836796 }, { -96.916358, 32.836774 }, { -96.915003, 32.836767 }}, {{ -96.914831, 32.837297 }, { -96.914872, 32.836663 }, { -96.915033, 32.835872 }, { -96.91514, 32.83546 }, { -96.915226, 32.835212 }, { -96.915853, 32.8337 }, { -96.91595, 32.833443 }}, {{ -96.915092, 32.836889 }, { -96.916398, 32.836891 }, { -96.919547, 32.836963 }, { -96.921322, 32.836952 }, { -96.921387, 32.836952 }}, {{ -96.9104, 32.837008 }, { -96.910671, 32.836977 }, { -96.9111, 32.836941 }, { -96.911543, 32.836925 }, { -96.912637, 32.836898 }, { -96.913501, 32.836886 }, { -96.914528, 32.83688 }}, {{ -96.91577, 32.833443 }, { -96.915671, 32.833693 }, { -96.915344, 32.834518 }, { -96.91481, 32.835911 }, { -96.914754, 32.836127 }, { -96.914657, 32.836659 }, { -96.914617, 32.83752 }, { -96.914595, 32.838561 }, { -96.914603, 32.839688 }, { -96.914587, 32.840693 }, { -96.914512, 32.841211 }, { -96.914421, 32.841666 }, { -96.914284, 32.842196 }, { -96.914097, 32.842674 }}, {{ -96.914316, 32.840555 }, { -96.915253, 32.840564 }}, {{ -96.916862, 32.837164 }, { -96.917377, 32.837281 }}, {{ -96.915129, 32.836607 }, { -96.91441, 32.836582 }}, {{ -96.915263, 32.840458 }, { -96.914324, 32.840454 }}, {{ -96.914528, 32.83688 }, { -96.914987, 32.836886 }}, {{ -96.915003, 32.836767 }, { -96.914526, 32.836763 }}}; +static const cr::line_string points = {{ -96.920341, 32.838261 }, { -96.920421, 32.838295 }, { -96.920421, 32.838295 }, { -96.920536, 32.838297 }, { -96.920684, 32.838293 }, { -96.920818, 32.838342 }, { -96.920349, 32.838306 }, { -96.920421, 32.838295 }, { -96.919874, 32.837479 }, { -96.920097, 32.837684 }, { -96.92018, 32.837844 }, { -96.92029, 32.838216 }, { -96.920341, 32.838261 }, { -96.915781, 32.834689 }, { -96.915735, 32.834529 }, { -96.91573, 32.834443 }, { -96.915733, 32.834286 }, { -96.915853, 32.8337 }, { -96.915092, 32.836889 }, { -96.915915, 32.836977 }, { -96.914617, 32.83752 }, { -96.914507, 32.838106 }, { -96.914319, 32.839063 }, { -96.915671, 32.833443 }, { -96.915427, 32.833817 }, { -96.91532, 32.833963 }, { -96.915247, 32.834072 }, { -96.915062, 32.834252 }, { -96.914775, 32.834518 }, { -96.9104, 32.837119 }, { -96.910567, 32.837069 }, { -96.9111, 32.836941 }, { -96.91441, 32.836582 }, { -96.914225, 32.836544 }, { -96.914158, 32.836499 }, { -96.914107, 32.836449 }, { -96.914075, 32.836393 }, { -96.914059, 32.836292 }, { -96.914091, 32.836073 }, { -96.914649, 32.834919 }, { -96.914837, 32.834601 }, { -96.914858, 32.834583 }, { -96.914912, 32.834563 }, { -96.914968, 32.834561 }, { -96.91503, 32.83457 }, { -96.915081, 32.834592 }, { -96.915116, 32.834628 }, { -96.915134, 32.834671 }, { -96.91514, 32.834718 }, { -96.915124, 32.834793 }, { -96.914931, 32.835336 }, { -96.914888, 32.835509 }, { -96.91481, 32.835911 }, { -96.914155, 32.836758 }, { -96.913391, 32.836697 }, { -96.912991, 32.836643 }, { -96.912847, 32.836589 }, { -96.912761, 32.836508 }, { -96.912573, 32.836433 }, { -96.917377, 32.837281 }, { -96.917589, 32.837317 }, { -96.918117, 32.837339 }, { -96.920815, 32.836594 }, { -96.919732, 32.836643 }, { -96.918975, 32.836702 }, { -96.918077, 32.836729 }, { -96.916661, 32.83672 }, { -96.916358, 32.836774 }, { -96.911543, 32.836925 }, { -96.911806, 32.837006 }, { -96.911994, 32.837028 }, { -96.912326, 32.837114 }, { -96.912552, 32.837202 }, { -96.912656, 32.83729 }, { -96.914657, 32.836659 }, { -96.914518, 32.837522 }, { -96.914472, 32.837673 }, { -96.914383, 32.837772 }, { -96.914284, 32.837858 }, { -96.914153, 32.837905 }, { -96.914024, 32.837912 }, { -96.913925, 32.837885 }, { -96.913804, 32.837828 }, { -96.913678, 32.837729 }, { -96.913391, 32.837479 }, { -96.913278, 32.837328 }, { -96.913252, 32.837238 }, { -96.913262, 32.837148 }, { -96.913305, 32.837078 }, { -96.91337, 32.837024 }, { -96.91348, 32.837004 }, { -96.914483, 32.83688 }, { -96.915915, 32.836977 }, { -96.916183, 32.837004 }, { -96.916425, 32.837042 }, { -96.916862, 32.837164 }, { -96.915183, 32.840102 }, { -96.915151, 32.839929 }, { -96.915113, 32.839539 }, { -96.915008, 32.838973 }, { -96.914949, 32.83873 }, { -96.914909, 32.838491 }, { -96.914834, 32.838234 }, { -96.914728, 32.842674 }, { -96.914772, 32.84257 }, { -96.914872, 32.842277 }, { -96.914974, 32.841939 }, { -96.915014, 32.841646 }, { -96.915065, 32.84138 }, { -96.915113, 32.84099 }, { -96.915175, 32.840321 }, { -96.915193, 32.839827 }, { -96.915204, 32.839345 }, { -96.91525, 32.839045 }, { -96.915325, 32.838759 }, { -96.915483, 32.838464 }, { -96.915658, 32.838236 }, { -96.915907, 32.837943 }, { -96.91621, 32.837709 }, { -96.91636, 32.837612 }, { -96.916588, 32.837481 }, { -96.916948, 32.837324 }, { -96.91731, 32.837188 }, { -96.918026, 32.837098 }, { -96.918847, 32.83706 }, { -96.919391, 32.83704 }, { -96.919847, 32.83701 }, { -96.920174, 32.836961 }, { -96.914603, 32.839688 }, { -96.914335, 32.841087 }, { -96.914099, 32.841896 }, { -96.913823, 32.842577 }, { -96.91377, 32.842674 }, { -96.915915, 32.836977 }, { -96.916105, 32.837042 }, { -96.916183, 32.837116 }, { -96.916218, 32.837164 }, { -96.916232, 32.83722 }, { -96.916223, 32.837308 }, { -96.916202, 32.837355 }, { -96.916006, 32.837556 }, { -96.915714, 32.837779 }, { -96.915588, 32.83784 }, { -96.915419, 32.837871 }, { -96.915293, 32.837867 }, { -96.91514, 32.837828 }, { -96.915062, 32.837774 }, { -96.915014, 32.837734 }, { -96.914925, 32.837614 }, { -96.914888, 32.837515 }, { -96.914831, 32.837297 }, { -96.917743, 32.833443 }, { -96.917766, 32.836553 }, { -96.912399, 32.842674 }, { -96.911487, 32.841373 }, { -96.9104, 32.841399 }, { -96.91072, 32.84264 }, { -96.910717, 32.842674 }, { -96.911584, 32.833443 }, { -96.911581, 32.833684 }, { -96.911559, 32.835271 }, { -96.911554, 32.83642 }, { -96.920403, 32.833443 }, { -96.920397, 32.833894 }, { -96.9104, 32.833662 }, { -96.911581, 32.833684 }, { -96.914775, 32.834518 }, { -96.914512, 32.835038 }, { -96.914327, 32.835437 }, { -96.914016, 32.836136 }, { -96.913973, 32.83621 }, { -96.913906, 32.83628 }, { -96.913769, 32.836359 }, { -96.913675, 32.836384 }, { -96.913203, 32.836445 }, { -96.9104, 32.837231 }, { -96.910695, 32.837202 }, { -96.911025, 32.837182 }, { -96.911355, 32.837168 }, { -96.911948, 32.837168 }, { -96.912391, 32.837222 }, { -96.912656, 32.83729 }, { -96.913072, 32.83752 }, { -96.916189, 32.83818 }, { -96.918117, 32.837339 }, { -96.918165, 32.84179 }, { -96.917329, 32.840988 }, { -96.91702, 32.840704 }, { -96.916811, 32.840587 }, { -96.916661, 32.840519 }, { -96.916368, 32.840454 }, { -96.916164, 32.840438 }, { -96.915263, 32.840458 }, { -96.914137, 32.840553 }, { -96.914316, 32.840555 }, { -96.91415, 32.840452 }, { -96.914054, 32.841182 }, { -96.913976, 32.841572 }, { -96.913842, 32.842049 }, { -96.913691, 32.842414 }, { -96.913569, 32.842674 }, { -96.914919, 32.842674 }, { -96.91492, 32.842671 }, { -96.9151, 32.842171 }, { -96.915244, 32.84152 }, { -96.915322, 32.840979 }, { -96.915376, 32.840569 }, { -96.915384, 32.840454 }, { -96.915474, 32.833443 }, { -96.915379, 32.833664 }, { -96.915314, 32.833772 }, { -96.915261, 32.833849 }, { -96.915081, 32.834083 }, { -96.914775, 32.834518 }, { -96.913072, 32.83752 }, { -96.913195, 32.837608 }, { -96.913675, 32.838002 }, { -96.913694, 32.838011 }, { -96.91396, 32.838241 }, { -96.914054, 32.838345 }, { -96.914161, 32.838507 }, { -96.914255, 32.838714 }, { -96.914292, 32.83889 }, { -96.914319, 32.839063 }, { -96.914182, 32.840098 }, { -96.91415, 32.840452 }, { -96.917406, 32.836557 }, { -96.91588, 32.836517 }, { -96.915778, 32.836501 }, { -96.915727, 32.836481 }, { -96.91569, 32.836458 }, { -96.915644, 32.836422 }, { -96.915609, 32.836382 }, { -96.915588, 32.836348 }, { -96.915572, 32.836305 }, { -96.915564, 32.836235 }, { -96.915564, 32.836154 }, { -96.915668, 32.835469 }, { -96.915754, 32.834827 }, { -96.915781, 32.834689 }, { -96.915835, 32.834556 }, { -96.916052, 32.834074 }, { -96.916283, 32.833443 }, { -96.915384, 32.840454 }, { -96.915408, 32.839794 }, { -96.915411, 32.839345 }, { -96.915459, 32.839068 }, { -96.91554, 32.838845 }, { -96.915636, 32.838687 }, { -96.915765, 32.838529 }, { -96.915945, 32.838358 }, { -96.916189, 32.83818 }, { -96.919421, 32.837159 }, { -96.919557, 32.837216 }, { -96.919697, 32.837288 }, { -96.919812, 32.8374 }, { -96.919874, 32.837479 }, { -96.920172, 32.838358 }, { -96.920295, 32.838845 }, { -96.920333, 32.839068 }, { -96.920327, 32.839422 }, { -96.920223, 32.839746 }, { -96.920072, 32.840046 }, { -96.919898, 32.840276 }, { -96.919812, 32.840355 }, { -96.919426, 32.84067 }, { -96.918012, 32.84172 }, { -96.91753, 32.84207 }, { -96.917374, 32.842196 }, { -96.916727, 32.842674 }, { -96.916981, 32.842674 }, { -96.917079, 32.842602 }, { -96.917487, 32.842291 }, { -96.918114, 32.841826 }, { -96.919984, 32.840454 }, { -96.920236, 32.840233 }, { -96.920429, 32.839965 }, { -96.920512, 32.839816 }, { -96.92059, 32.839557 }, { -96.920612, 32.839271 }, { -96.920604, 32.839181 }, { -96.920574, 32.839043 }, { -96.920416, 32.83857 }, { -96.920365, 32.83839 }, { -96.920341, 32.838261 }, { -96.9203, 32.838119 }, { -96.920282, 32.837943 }, { -96.920274, 32.83775 }, { -96.920295, 32.837603 }, { -96.920319, 32.837536 }, { -96.920397, 32.837407 }, { -96.920515, 32.837288 }, { -96.920601, 32.837227 }, { -96.920727, 32.83717 }, { -96.91441, 32.836582 }, { -96.914188, 32.836571 }, { -96.913203, 32.836445 }, { -96.914324, 32.840454 }, { -96.91415, 32.840452 }, { -96.921387, 32.836595 }, { -96.920815, 32.836594 }, { -96.917766, 32.836553 }, { -96.917406, 32.836557 }, { -96.915851, 32.836621 }, { -96.915459, 32.836621 }, { -96.915129, 32.836607 }, { -96.918117, 32.837339 }, { -96.918324, 32.837252 }, { -96.918586, 32.837186 }, { -96.918844, 32.837161 }, { -96.919421, 32.837159 }, { -96.920727, 32.83717 }, { -96.921387, 32.83717 }, { -96.913203, 32.836445 }, { -96.913142, 32.836438 }, { -96.912999, 32.836438 }, { -96.912774, 32.836427 }, { -96.912573, 32.836433 }, { -96.911554, 32.83642 }, { -96.9104, 32.836416 }, { -96.915253, 32.840564 }, { -96.915376, 32.840569 }, { -96.915816, 32.840562 }, { -96.916197, 32.840567 }, { -96.916478, 32.840598 }, { -96.916623, 32.840641 }, { -96.916757, 32.840702 }, { -96.916913, 32.840794 }, { -96.917326, 32.841157 }, { -96.917452, 32.841272 }, { -96.917956, 32.841761 }, { -96.918061, 32.841867 }, { -96.9145, 32.842674 }, { -96.914547, 32.842534 }, { -96.914708, 32.841971 }, { -96.914802, 32.841473 }, { -96.914858, 32.841078 }, { -96.914882, 32.840742 }, { -96.914907, 32.840224 }, { -96.914915, 32.839706 }, { -96.914885, 32.839084 }, { -96.914826, 32.83811 }, { -96.914831, 32.837297 }, { -96.914987, 32.836886 }, { -96.915092, 32.836889 }, { -96.914526, 32.836763 }, { -96.914155, 32.836758 }, { -96.9104, 32.836758 }, { -96.921387, 32.836813 }, { -96.918825, 32.836796 }, { -96.916358, 32.836774 }, { -96.915003, 32.836767 }, { -96.914831, 32.837297 }, { -96.914872, 32.836663 }, { -96.915033, 32.835872 }, { -96.91514, 32.83546 }, { -96.915226, 32.835212 }, { -96.915853, 32.8337 }, { -96.91595, 32.833443 }, { -96.915092, 32.836889 }, { -96.916398, 32.836891 }, { -96.919547, 32.836963 }, { -96.921322, 32.836952 }, { -96.921387, 32.836952 }, { -96.9104, 32.837008 }, { -96.910671, 32.836977 }, { -96.9111, 32.836941 }, { -96.911543, 32.836925 }, { -96.912637, 32.836898 }, { -96.913501, 32.836886 }, { -96.914528, 32.83688 }, { -96.91577, 32.833443 }, { -96.915671, 32.833693 }, { -96.915344, 32.834518 }, { -96.91481, 32.835911 }, { -96.914754, 32.836127 }, { -96.914657, 32.836659 }, { -96.914617, 32.83752 }, { -96.914595, 32.838561 }, { -96.914603, 32.839688 }, { -96.914587, 32.840693 }, { -96.914512, 32.841211 }, { -96.914421, 32.841666 }, { -96.914284, 32.842196 }, { -96.914097, 32.842674 }, { -96.914316, 32.840555 }, { -96.915253, 32.840564 }, { -96.916862, 32.837164 }, { -96.917377, 32.837281 }, { -96.915129, 32.836607 }, { -96.91441, 32.836582 }, { -96.915263, 32.840458 }, { -96.914324, 32.840454 }, { -96.914528, 32.83688 }, { -96.914987, 32.836886 }, { -96.915003, 32.836767 }, { -96.914526, 32.836763 }}; diff --git a/third_party/cheap-ruler-cpp-2.5.4/test/fixtures/turf.hpp b/third_party/cheap-ruler-cpp-2.5.4/test/fixtures/turf.hpp new file mode 100644 index 00000000000..42301aa97bc --- /dev/null +++ b/third_party/cheap-ruler-cpp-2.5.4/test/fixtures/turf.hpp @@ -0,0 +1,1894 @@ +#pragma once + +#include + +// Pre-computed test expectations with turf.js + +namespace cr = mapbox::cheap_ruler; + +static const std::vector turf_distance = { + 0.008378522721530966, + 0, + 0.010749717813535198, + 0.013838610971284946, + 0.013657697587927904, + 0.044013281621164545, + 0.0068391514197623125, + 0.10416972567189697, + 0.030891354635076577, + 0.019413765237647276, + 0.04263542169525046, + 0.0069116261879168155, + 0.582645708508002, + 0.018308680637617696, + 0.009577172911626559, + 0.017465334500943574, + 0.06613851306063838, + 0.3617719377192654, + 0.07753561751121403, + 0.13551122988625394, + 0.06598639029222791, + 0.10788718733352708, + 0.637754036319657, + 0.04744046409407494, + 0.019071666981656257, + 0.013911884201543205, + 0.026453803385838824, + 0.03993584290435518, + 0.5008820538480422, + 0.016568611251168885, + 0.05180738530903197, + 0.3119106296673429, + 0.017798808015935193, + 0.008016350616268752, + 0.007324494221885543, + 0.006909617037766451, + 0.01133329359318173, + 0.024542232038179984, + 0.13854858779974746, + 0.039494712699750543, + 0.002803669222998928, + 0.005515366276063487, + 0.005238469974132169, + 0.005880339789619507, + 0.005357898801346142, + 0.0051705154162532425, + 0.005070109623060704, + 0.0052577908286051914, + 0.00847519939173927, + 0.06303372152921366, + 0.019657925611790725, + 0.045304719345077606, + 0.11235257808731806, + 0.07172313743043487, + 0.03786245739823775, + 0.014737426160375995, + 0.012073619242440572, + 0.01944992465839253, + 0.4587699541978461, + 0.02021344170822001, + 0.04940583279333197, + 0.26541509069849073, + 0.10136117015958783, + 0.071051047335519, + 0.0839785094261126, + 0.13233952591634118, + 0.028947596386964588, + 0.450310288271311, + 0.02617846959013443, + 0.01773953006642692, + 0.03246883345774803, + 0.023279127631702524, + 0.013794142444291755, + 0.19974484175602678, + 0.0968663637124452, + 0.017337159580454113, + 0.013800054342251185, + 0.013308139694942028, + 0.01331222080564702, + 0.012080965868250661, + 0.009727373321527968, + 0.012964274265275947, + 0.016122067768646026, + 0.03863515912787102, + 0.019839914076975624, + 0.010301365066689631, + 0.010054214744799303, + 0.008762005427975264, + 0.008542780137735616, + 0.010518226790403411, + 0.09474686301917724, + 0.1342649575551186, + 0.02522592703114516, + 0.023008194493208647, + 0.04303616022369959, + 0.36251220899321573, + 0.019473754396392998, + 0.043524753809585405, + 0.06371623663872246, + 0.02758552742766217, + 0.026845476675765343, + 0.029432835806681268, + 0.4939597997561797, + 0.012276958817933555, + 0.03390371747191658, + 0.03878525490806829, + 0.03280401819558517, + 0.02996855089043349, + 0.043610944953640665, + 0.07463799480713804, + 0.054973280670314324, + 0.05362263439641399, + 0.03364472557328668, + 0.03257474948646934, + 0.035982127999637514, + 0.03017661243030025, + 0.04004558186048798, + 0.03846182853667192, + 0.017689723726582205, + 0.02581380073796066, + 0.03790654917278124, + 0.0370593711860446, + 0.06765988226898403, + 0.07684458549068582, + 0.05088930944950997, + 0.04274689073720004, + 0.031042698873467, + 0.6025557457581485, + 0.15761319510469404, + 0.09264826777038944, + 0.08001841776554604, + 0.011871825072563895, + 0.664627433433829, + 0.01917232375926572, + 0.01099493090349843, + 0.006261363451393286, + 0.0063648042581234765, + 0.009824297643237445, + 0.005584056437412894, + 0.028902860335050025, + 0.03687758065157599, + 0.013590391879516907, + 0.016166117999544854, + 0.011783888353457304, + 0.01494235041421174, + 0.009445375968215059, + 0.0063181221302946585, + 0.015727070675584043, + 0.011541912177440836, + 0.024826353234049702, + 0.5077727699239808, + 0.345931459929503, + 0.845643164225681, + 0.16794289804370816, + 0.10162406414747382, + 0.14123839370037056, + 0.003792191683109981, + 1.0299547040985766, + 0.026807856101721112, + 0.17653371969231604, + 0.12780393266727977, + 0.8908493894032292, + 0.05016778892928127, + 0.934681359703447, + 0.1104041562314812, + 0.31259370205263126, + 0.06284566928645681, + 0.0476296971944526, + 0.0830049159968845, + 0.009159661389404805, + 0.009991588429808441, + 0.015528991567811172, + 0.009214628598242817, + 0.04463082981364765, + 0.2761646624698818, + 0.02775785294501277, + 0.030920921663565613, + 0.030880088364676584, + 0.055419986988230405, + 0.041834856384027466, + 0.02589530670378627, + 0.046540107091908624, + 0.3004110613744119, + 0.20301909742424815, + 0.4951043096230534, + 0.11858115408807053, + 0.04279910991149764, + 0.023470200843156975, + 0.01592837521137288, + 0.028320290449247595, + 0.01914740566847937, + 0.08423100623656579, + 0.10575781800339631, + 0.016729639837999192, + 0.01928515125899576, + 0.08169190304672652, + 0.043987800607954874, + 0.054514411329818525, + 0.04298134155196387, + 0.031085953544609714, + 0.12615909276179177, + 0.0003465282173637971, + 0.058103130282949, + 0.07365046853595525, + 0.060615225341305666, + 0.04588259821425726, + 0.012813260692314313, + 0.7798777201137593, + 0.02613612797608842, + 0.013461529716750041, + 0.009893951634252595, + 0.030991193644600953, + 0.05620493774258541, + 0.3699038744925965, + 0.015097946393717242, + 0.06271302769749793, + 0.0020384128886200997, + 0.03567165517083371, + 0.014525490144668496, + 0.020607956055678806, + 0.0246435390331219, + 0.019879489642093223, + 0.019407494266871623, + 0.11583263922549474, + 0.03948876108912776, + 0.5294244867375493, + 0.1426857432967834, + 0.009697385429695126, + 0.00525992849031739, + 0.00430141196125398, + 0.005875039927428631, + 0.005522219552694836, + 0.0042607480963535544, + 0.005011184230011202, + 0.007821903345226908, + 0.009009616491770123, + 0.07680989084065051, + 0.07186045769829646, + 0.015555751145719514, + 0.015630740713881576, + 0.057320516556811474, + 0.07343146368625852, + 0.7843452939662691, + 0.07344594465684656, + 0.04994298205930179, + 0.031135505320641476, + 0.025933641798745768, + 0.01973190434263985, + 0.021311914846065032, + 0.025391967733273624, + 0.030199067305597543, + 0.3226950750643045, + 0.014203685630096484, + 0.01534037036997655, + 0.016453107333170788, + 0.010525597809363562, + 0.10166018086042491, + 0.05537516038147018, + 0.025057187459666765, + 0.03937935331770495, + 0.03732606414021839, + 0.03623014247507323, + 0.0303133933027351, + 0.01190830530874745, + 0.05028800045819346, + 0.17635692976160655, + 0.05953594686309811, + 0.020222535641001925, + 0.0805146256571518, + 0.02373659967645878, + 0.012165928750908704, + 0.0514820122407643, + 0.07815653280919697, + 0.23201058369437041, + 0.0340423547943379, + 0.03484151181059308, + 0.018298598649656068, + 0.029716445173173824, + 0.03187810305202435, + 0.010038564502119844, + 0.015603661528300027, + 0.0546445315444071, + 0.0205808711039938, + 0.014522895203654958, + 0.016252765900716146, + 0.019648594042289334, + 0.021480373184972493, + 0.016468148859886997, + 0.007782615606278065, + 0.016094168571066846, + 0.017228336077233373, + 0.01051829618341627, + 0.013373893984522116, + 0.5939814038121429, + 0.020783632669147317, + 0.09311654260907076, + 0.45806162186901805, + 0.016262433777165904, + 0.8009278300362112, + 0.05345785152641949, + 0.2849887196254366, + 0.033647685486223135, + 0.14550079382917516, + 0.03663536045894266, + 0.030880282999975885, + 0.29087772423039293, + 0.021630884822714493, + 0.02556252999484156, + 0.024271715891829745, + 0.05392513908839965, + 0.12206094837643991, + 0.06168160301572301, + 0.7690944309219785, + 0.005753846170923199, + 0.01336445751002951, + 0.02106355972240177, + 0.01879685972329176, + 0.0952444313427708, + 0.1078511795464463, + 0.6469708160164086, + 0.011508214196305674, + 0.04112686678910598, + 0.035610088844009874, + 0.026485811067702324, + 0.014370051474837841, + 0.014242735913957073, + 0.017811674662530638, + 0.05585618250055378, + 0.01738598064185313, + 0.07195021868945746, + 0.015339399943488055, + 0.34467463670324133, + 0.016179746130454448, + 0.06440448684565248, + 0.05608468433498269, + 0.04424636779988501, + 0.037440464051362185, + 0.057664402696718915, + 0.05762190400375552, + 0.06924174005124464, + 0.10847808154508215, + 0.0904310617110173, + 0.047983949715355446, + 0.009818685405935522, + 0.054721988898462984, + 0.034677157404038546, + 0.3509325669208246, + 1.0268344467822377, + 0.23944522020834375, + 0.23057234097441315, + 0.12663716357349866, + 0.06110408862755362, + 0.0706237379606926, + 0.08926016309905949, + 0.04690508445469457, + 0.028732090191940274, + 0.17809611583512075, + 0.029989165393382068, + 0.391596040835184, + 0.12205539606742612, + 0.29440576356307174, + 0.16589103850643355, + 0.006074718243210164, + 1.0268328659759098, + 0.02556053511771133, + 0.04029260310233885, + 0.041439782865155726, + 0.10228632526932319, + 0.08075810970675557, + 0.09598294305539697, + 0.39953051109714005, + 0.029306409486832358, + 0.09671994873655325, + 0.1627823795415789, + 0.02458907783680164, + 0.05986464703306779, + 0.09584181947018754, + 0.11580850896272278, + 0.12535825153862024, + 0.11179598232834778, + 0.058041800368261356, + 0.05131909973307807, + 0.0603260376812227, + 0.05596616469177014, + 0.23658290631261256, + 0.0875715634398609, + 0.40697930337242394, + 0.04985868432498091, + 0.2230670459299213, + 0.06725351272770712, + 0.4384349898078352, + 0.08775397845174246, + 0.3979923162137123, + 0.04290208100438889, + 0.013320545764006694, + 0.04458140027621235 +}; + +static const std::vector turf_bearing = { + -63.16835850131531, + 0, + -88.81417863186009, + -91.84236975943857, + -66.48043977763521, + 95.21979003833424, + -100.30570881383704, + 150.61043051772228, + -42.426806215464694, + -23.550372000573976, + -13.952505152879574, + -43.59833577993832, + 132.99198010189937, + 166.41929543157306, + 177.20325520034712, + -179.08016176339177, + -170.23703380143226, + 11.337660289434671, + -82.74731094370897, + 63.53130497081162, + 8.962738746772079, + 9.37241746920313, + -168.57228731224598, + 28.730760752589035, + 31.624597764846637, + 29.367864287830926, + 40.81330085442227, + 42.194625737959534, + 54.716993037210045, + -109.61289479694872, + -105.95081303007647, + -97.35442495838333, + 103.73738360872167, + 128.63753291604394, + 139.40255086492127, + 154.353076115606, + 172.41822883056525, + -173.00067170261374, + -157.88885299734986, + -153.5843194614746, + -135.57055814036255, + -113.78745742707413, + -92.43386994136272, + -80.19821514884795, + -62.824367973789435, + -39.24535094117481, + -19.378190579597625, + -6.122391240486928, + 10.162381849878825, + 16.628046997768607, + 11.796298622071708, + 9.259418658966705, + 33.013988074027225, + 95.42809952694313, + 99.12772053947573, + 114.05171960306723, + 138.26425923091327, + 115.39825807756951, + -78.13409883483345, + -78.57409383589719, + -87.160852199759, + -108.19195718984352, + 86.91738524340066, + 84.70017282115018, + 87.95033104208615, + 90.43302815253836, + 78.02446742577152, + 87.86117672643189, + -69.86932102730815, + -82.07107484952823, + -72.86553078555735, + -65.13553755018475, + -44.79817086959291, + -110.571105457802, + 7.7069914618379975, + 14.357081966766243, + 37.06518971504373, + 44.04525332938273, + 66.87692305523359, + 86.3047355297261, + 107.98313958114178, + 119.27773275782387, + 133.08034139008478, + 136.03334417898122, + 147.83958090135633, + 166.35653589386422, + -174.66648287441066, + -152.70026365512155, + -134.67606484413284, + -102.21019447457768, + -98.37013738611591, + -85.39045487355833, + -83.16251407128772, + -79.41426027366361, + -71.6199202393354, + 25.647805187311675, + 171.1662911122707, + 175.31992210998703, + 171.14075667901446, + 168.46991110160218, + 171.99557069100132, + 166.22310505740552, + 1.1490813990525728, + -160.4320410335643, + -163.99994946634425, + -165.7728900157161, + -173.45680597027817, + -170.84905103756142, + -174.09621979366605, + -175.5476650357732, + -178.24648292689753, + -178.90151317182918, + -172.65900564120977, + -167.57442915983086, + -155.7719187979583, + -147.18225435271103, + -144.4718825804975, + -132.58756100092145, + -127.58358614976032, + -124.36541206185082, + -117.43139970416347, + -114.09107335076804, + -98.50829769899121, + -93.15284638637799, + -92.50530269529919, + -94.47703726903666, + -100.11192008297043, + 59.77360017983539, + 9.143285027353551, + 13.771320155516808, + 18.804056386135507, + 24.65786911308088, + -162.44500486120558, + -67.84560294942295, + -41.52913748098395, + -31.493999863996763, + -11.862722004156867, + 4.911415384854547, + 20.5768593094366, + 39.328103571820634, + 47.73104506840779, + 60.04949187845642, + 77.68453534325027, + 92.16377287776062, + 106.8766740367609, + 129.48750446573763, + 134.76457530705628, + 148.07063441375706, + 162.56674425873655, + 167.60958213096342, + -147.58932438111154, + -0.35602290379352103, + 36.377431696616014, + 149.50345988748333, + 88.36898545914883, + -12.223747274643541, + 4.239696760480642, + -175.4876519466847, + 0.5992644178179116, + 0.6673420115819962, + 0.2094907113015796, + -111.81827272749739, + 0.6404507036839922, + 91.57935182920652, + -88.72964175221959, + -72.73595760634777, + 23.023787324470447, + 21.284935274447214, + 20.497437869075558, + 26.023401004269186, + 38.80671567232787, + 55.538280861535505, + 72.4357211583551, + 81.25555219912327, + 71.54335789800336, + -96.67318521343546, + -94.1256029846292, + -92.8904417068675, + -89.99983922215844, + -81.74515005128639, + -73.017109535514, + -56.65379972139568, + -75.85450215095052, + -117.43602194323526, + -0.5191133757264856, + 138.78810984843733, + 137.56824257563747, + 123.6752409593202, + 118.34970554664147, + 104.79079949099777, + 95.33306354879424, + 88.48636023444747, + 84.26539611797256, + -89.23804769018248, + 126.44610739090022, + 6.304981289653255, + 9.538566926163998, + 13.280042951632822, + 19.166182380726028, + 21.51585631114597, + -89.9996339244023, + -164.35486815306686, + -163.17151075342585, + -169.4719802936045, + -173.09313538953987, + -173.68544129286698, + -176.6550060133922, + -179.38201633291828, + 19.85934450173753, + 26.825916744943097, + 30.042999793582226, + 32.87625705680969, + 30.58584499722022, + 25.48436330531482, + -49.58528537111213, + -45.66818093662166, + -60.58696099977996, + -44.178097235571066, + -37.213574067968544, + -29.02801030830766, + -20.88386390872622, + -10.016986251287301, + -7.470516078862877, + 6.345968960148908, + 4.343209329511361, + -144.91638104371413, + 91.78645944829398, + 100.57490882565328, + 115.0198771218778, + 126.49515676679046, + 132.9667450433102, + 143.6768700122894, + 152.57244080955854, + 162.6386056371201, + 174.5149571496929, + 180, + -172.73016111884994, + -173.57809345551524, + -170.66439195019734, + -161.16292504839234, + -159.27915118031842, + -162.90191364007953, + 6.149002598385885, + -178.25002252904207, + -179.67835798380287, + -171.71629870718354, + -163.02862431499696, + -152.95560774414605, + -145.55034082696017, + -138.50963007714628, + -130.96610418046026, + -110.60445245954395, + -63.488932754631335, + -58.5296285047684, + -40.784935262998765, + -33.4011304023945, + -15.899450270318724, + -11.98079922175284, + -8.147813628278488, + 0.8158694642281521, + 15.093060659786769, + 22.923229163325363, + 32.44078804116013, + 42.447012241263664, + 45.83427921173283, + 48.52837549516453, + 49.16371967470212, + 46.12898587570745, + 48.67322750744069, + -89.99993112361511, + -131.16855313987196, + -132.21634274729973, + -131.43505191524252, + -131.12876979883848, + -136.22759997049272, + -148.82348281194703, + -154.91921385645844, + -165.80042858245199, + -176.30209430550704, + 175.72884637650225, + 169.64893781173248, + 164.32272447851253, + 166.609539892841, + 171.1155850861851, + 166.3637807513156, + 175.0886195159549, + 178.0053467715306, + -173.15552476803634, + -163.24971275122377, + -153.06780391136738, + -140.2005052574728, + -130.17077601985082, + -118.29848203781941, + 96.31992932289273, + 93.37488183764101, + 98.65620852739153, + -13.220894289645209, + 90.78374679182464, + -122.38574263089491, + 90.11906042024525, + 90.91606631491278, + 89.24226505678728, + 87.1952301524552, + 89.99989371956251, + 92.89042347389294, + -73.7442223661134, + -116.5749747098209, + -106.68941539794253, + -96.5786003424879, + -90.23620894320432, + -89.42530845705083, + -89.99982105641459, + 96.0164612641722, + 97.77709140355806, + 89.99996123049279, + 93.32997836025105, + 87.96524189628325, + 90.86961377588838, + 90.23605053342526, + -44.50763881494917, + -87.22999594921937, + -91.08466221917038, + -89.10502963081117, + -82.51948924863764, + -70.55890852242291, + -61.55042515193161, + -54.93400664520933, + -43.708432529297596, + -42.630819035108374, + -40.89066031737039, + -39.768718845646205, + 74.90365235947708, + -164.2486209384613, + -166.49009116775852, + -170.9886716825588, + -173.2072838154825, + -176.56563073545988, + -177.67795564677132, + -179.256576114407, + 177.6794175196906, + 177.08641995168978, + -179.7039343413944, + -162.31174704216053, + -88.05239082476302, + 104.83933395089112, + 90.91884342553296, + 89.99898193057349, + -89.65566421438635, + 90.45177683075481, + 90.60742069201243, + 90.35190900335593, + 15.252229486307309, + -176.88983601147055, + -170.29517886206932, + -167.69014730626628, + -163.75539093753457, + -160.78980004585748, + -162.40423641636087, + 11.815918282011998, + -89.89521786054206, + -88.44037032865228, + -90.4221071958824, + -89.99998237874593, + 89.64945709813418, + -97.7527834928843, + -95.70334593407169, + -92.4612675583479, + -91.68219351312248, + -90.94678497047079, + -90.39810804835399, + -163.10988605669783, + 18.40425036657263, + 18.419844554057384, + 17.853582297033203, + 12.289104432473655, + 8.709879040159981, + 2.2353614586368535, + 1.0172614149903392, + -0.34171312935348624, + 0.7663441543155729, + 6.935793519460202, + 9.538556916896354, + 12.252956857370524, + 18.19480908504452, + -175.0372577909854, + -89.34475877505189, + -158.31621119967375, + -74.8695322495171, + 109.63776192527479, + 92.36950183161946, + -10.475734036813332, + 90.2902405855761, + -177.25427215646803, + -89.10855416703986, + -173.55458838862484, + 90.5716881361661 +}; + +static const std::vector turf_destination = { + { -96.92034100000001, 32.82927060622736 }, + { -96.92060772614725, 32.829305975369245 }, + { -96.92079439543319, 32.82931008237797 }, + { -96.92109595102636, 32.829318926002884 }, + { -96.92143033606274, 32.82932450415998 }, + { -96.92175049436656, 32.82938581393253 }, + { -96.92146736784431, 32.82936485157204 }, + { -96.92172490097579, 32.82937161249924 }, + { -96.9213630235027, 32.8285760913059 }, + { -96.92177070843935, 32.82880428175528 }, + { -96.92203788361117, 32.82899017678576 }, + { -96.92233149855905, 32.82939076851167 }, + { -96.92256548517682, 32.82946704822662 }, + { -96.91818769656206, 32.82592900640833 }, + { -96.91832325986564, 32.825805632710264 }, + { -96.91849903664684, 32.82575891598141 }, + { -96.91868196749337, 32.82564384425943 }, + { -96.91898098457527, 32.82510240477685 }, + { -96.91839818728636, 32.8283385839573 }, + { -96.91939827432712, 32.82847636744433 }, + { -96.91827631973166, 32.82907174007224 }, + { -96.91834125479305, 32.82971268589303 }, + { -96.91832704128093, 32.8307271881736 }, + { -96.91985130136207, 32.82516722942136 }, + { -96.91977856038616, 32.825603791320425 }, + { -96.91984148408056, 32.8258148548345 }, + { -96.91993702946877, 32.82599140014963 }, + { -96.91991915091329, 32.826241406696084 }, + { -96.91979779876095, 32.826579853154904 }, + { -96.91558705439019, 32.82925571745485 }, + { -96.91591658029526, 32.829282976817495 }, + { -96.91661047207285, 32.829234607705295 }, + { -96.92007967053509, 32.82895758586908 }, + { -96.9200521621881, 32.82900388634131 }, + { -96.92014087867756, 32.829045483451324 }, + { -96.92024377268062, 32.82908435082749 }, + { -96.92036379723118, 32.829119461406734 }, + { -96.92049790317056, 32.829111787443004 }, + { -96.92067803914178, 32.82898830051573 }, + { -96.92138209750365, 32.82793197154348 }, + { -96.9217141663508, 32.827713770769876 }, + { -96.92187716351988, 32.82779766780548 }, + { -96.92207102285543, 32.827881631620734 }, + { -96.92226470339185, 32.82798563055431 }, + { -96.92246216268975, 32.828102632325134 }, + { -96.92264635970814, 32.82823460404209 }, + { -96.92281225401825, 32.828382512213935 }, + { -96.92295880519139, 32.828539322759674 }, + { -96.92309097382822, 32.82870200101877 }, + { -96.92319872369181, 32.82889451176149 }, + { -96.92312705780591, 32.829556819195616 }, + { -96.9232028626346, 32.82985088699453 }, + { -96.9232411573597, 32.83037567828073 }, + { -96.92269992821585, 32.83134715565532 }, + { -96.9220470103152, 32.83141228122331 }, + { -96.92175545672075, 32.831486016562685 }, + { -96.92171723372269, 32.83156132277095 }, + { -96.921734306369, 32.8316111604673 }, + { -96.92164664653896, 32.831668489803576 }, + { -96.9265483183977, 32.83265027046581 }, + { -96.92685511412476, 32.832821461731555 }, + { -96.92747508633266, 32.83298002241657 }, + { -96.9302621268373, 32.832372910939625 }, + { -96.92926537384224, 32.832561085283594 }, + { -96.92859171846636, 32.832760503071036 }, + { -96.92777413074916, 32.8329291215357 }, + { -96.92643558565632, 32.833062897544984 }, + { -96.92620707047061, 32.83326078761194 }, + { -96.92146356644531, 32.83355674791104 }, + { -96.92179503322653, 32.8337837342936 }, + { -96.92204845102395, 32.83395270229553 }, + { -96.9224428137804, 32.834186607151885 }, + { -96.92272809550946, 32.834423403814895 }, + { -96.92288827790685, 32.83466104696409 }, + { -96.92494226054892, 32.83418049103243 }, + { -96.92485328363847, 32.83519469016151 }, + { -96.92485407643412, 32.835497598333895 }, + { -96.92480870102905, 32.83574916926978 }, + { -96.92475014863298, 32.83598835649724 }, + { -96.92465640380966, 32.83618911335752 }, + { -96.92456145502362, 32.83635039301763 }, + { -96.92449329255402, 32.83647814848498 }, + { -96.92439990743493, 32.836576332621924 }, + { -96.92429828977875, 32.83663289816016 }, + { -96.92403241904934, 32.83653879771671 }, + { -96.92393731871984, 32.836543983800375 }, + { -96.92392597882305, 32.8366104088374 }, + { -96.92394738757146, 32.83667702518138 }, + { -96.92399854388823, 32.83676378512602 }, + { -96.92406844480034, 32.8368666409214 }, + { -96.92418009097368, 32.837003544787805 }, + { -96.92518146526312, 32.837036448933326 }, + { -96.92660860730805, 32.83729030555763 }, + { -96.9268684835029, 32.83747406688541 }, + { -96.92709910606104, 32.837668685163564 }, + { -96.92752148718918, 32.837947112682556 }, + { -96.92582495878467, 32.84104130174476 }, + { -96.92577181513958, 32.841024204874486 }, + { -96.92570941035605, 32.84078977453039 }, + { -96.92557675694083, 32.8403789633218 }, + { -96.92548692274416, 32.840290723966085 }, + { -96.92541287907572, 32.840206009324746 }, + { -96.92530063370805, 32.84010277239874 }, + { -96.92515475186458, 32.84469596626941 }, + { -96.92515515797638, 32.84474454442302 }, + { -96.9252083791398, 32.84460346030672 }, + { -96.92526044659823, 32.84441666763957 }, + { -96.9252473860237, 32.844274120357106 }, + { -96.92524221144033, 32.84415777262634 }, + { -96.92523092269901, 32.843916578859954 }, + { -96.92523052056168, 32.84339549372804 }, + { -96.9251830757581, 32.843048472162685 }, + { -96.92512558963632, 32.842712469390584 }, + { -96.92510010191579, 32.842557440932126 }, + { -96.92510061543786, 32.842415342624996 }, + { -96.92518115035102, 32.84226313063035 }, + { -96.9252757385188, 32.84217676144705 }, + { -96.92544139007872, 32.84202419192918 }, + { -96.92565814381041, 32.84192937929272 }, + { -96.92571903404281, 32.84197028113293 }, + { -96.92585506965108, 32.841975855439806 }, + { -96.92612027958023, 32.84195406060516 }, + { -96.92638469759416, 32.84195185543719 }, + { -96.92700035587634, 32.84199419917395 }, + { -96.92771828543378, 32.84208705149566 }, + { -96.92815651411966, 32.842196372537174 }, + { -96.92850407135272, 32.84229412289962 }, + { -96.92871998933195, 32.84237126366221 }, + { -96.92303556750609, 32.845222756364045 }, + { -96.92265144507935, 32.84674456312207 }, + { -96.9222967307011, 32.84767464651239 }, + { -96.92189950489244, 32.8484749696396 }, + { -96.92172276419579, 32.848689496153796 }, + { -96.9237410893001, 32.843108190296135 }, + { -96.923802546131, 32.84328701670827 }, + { -96.92374965829724, 32.843472940765764 }, + { -96.9236514626933, 32.84363092836846 }, + { -96.92353000280231, 32.84379494600556 }, + { -96.92338332178431, 32.84398896076638 }, + { -96.9232224556523, 32.84413994035114 }, + { -96.92288446231687, 32.844442853078014 }, + { -96.92244837426914, 32.8447656678973 }, + { -96.92217622162691, 32.8449243543985 }, + { -96.92185905928895, 32.84505088281731 }, + { -96.92158293218587, 32.84514022404786 }, + { -96.92127788623063, 32.84519234965051 }, + { -96.92104596927354, 32.84522723186053 }, + { -96.92084223019, 32.84527384359657 }, + { -96.92059571034834, 32.84523815846969 }, + { -96.92039946444064, 32.84522115078961 }, + { -96.92018153243328, 32.84508279557485 }, + { -96.92293075844712, 32.8413060685728 }, + { -96.92278981214417, 32.844490946195606 }, + { -96.91725748845279, 32.85068440564823 }, + { -96.91617826911245, 32.84945342488996 }, + { -96.91492269293347, 32.84954698255384 }, + { -96.91507279822012, 32.85085305805673 }, + { -96.91489851558394, 32.85094963157779 }, + { -96.91559254022775, 32.84177868407015 }, + { -96.91541578881139, 32.842077197188324 }, + { -96.91521892350433, 32.84371915343081 }, + { -96.91503791956701, 32.844920536056705 }, + { -96.92370969579733, 32.84199332911104 }, + { -96.92352559723263, 32.84249151740852 }, + { -96.9133495208793, 32.842304086578224 }, + { -96.91435055467473, 32.84236802303858 }, + { -96.91736376818267, 32.843241314011124 }, + { -96.91691918097388, 32.843797947525246 }, + { -96.91655185513237, 32.84423091241846 }, + { -96.91605785663661, 32.84496119834133 }, + { -96.91583122001299, 32.845063795761554 }, + { -96.91558001680603, 32.84515969596507 }, + { -96.91525830338438, 32.84526189106002 }, + { -96.91497913504723, 32.84530737397885 }, + { -96.9143215696874, 32.84538613848035 }, + { -96.91133267086113, 32.84618717915156 }, + { -96.91144147769786, 32.84617049140981 }, + { -96.91158505725988, 32.84616007150336 }, + { -96.91172846628058, 32.84615291651313 }, + { -96.91213476159362, 32.84615702435345 }, + { -96.912391, 32.84621239377265 }, + { -96.91246923814964, 32.84627902435345 }, + { -96.91269853223814, 32.84650491651311 }, + { -96.91562893644198, 32.84715807150331 }, + { -96.91737052114979, 32.846307491409796 }, + { -96.91723228122223, 32.85074617915096 }, + { -96.91621037304874, 32.84992913847948 }, + { -96.91571580146685, 32.84962737397773 }, + { -96.9153216256599, 32.84948989105859 }, + { -96.91498690323063, 32.84939869596327 }, + { -96.91450969112039, 32.84930779575933 }, + { -96.91412204438035, 32.84926319833858 }, + { -96.9130380189888, 32.84925191241467 }, + { -96.9117296694325, 32.849312947520374 }, + { -96.91172705571321, 32.84927831400496 }, + { -96.91138023411317, 32.849136023030674 }, + { -96.91110422918695, 32.84982408656827 }, + { -96.91084713208613, 32.850169517397084 }, + { -96.91053498353314, 32.85059932909671 }, + { -96.91020684510828, 32.85091453604562 }, + { -96.909908771174, 32.851122153415716 }, + { -96.9110838227085, 32.85106719716819 }, + { -96.91091104294216, 32.851006684047555 }, + { -96.91091850811969, 32.85044663157913 }, + { -96.91089125672022, 32.849733058059954 }, + { -96.91079932847259, 32.84912698255514 }, + { -96.9106847733917, 32.84864942489266 }, + { -96.91052563309422, 32.848464405656216 }, + { -96.9104503638796, 32.84138094620757 }, + { -96.91019122863675, 32.84152706857188 }, + { -96.909963680057, 32.84155779559022 }, + { -96.90974976319653, 32.84155515080656 }, + { -96.90941051524207, 32.841707158486976 }, + { -96.90894698098482, 32.84205784361321 }, + { -96.907088047852, 32.8449732318619 }, + { -96.90705712898402, 32.844972349651776 }, + { -96.90738505824653, 32.845275224047036 }, + { -96.90725393055232, 32.845190882816425 }, + { -96.90737174860608, 32.84532535439583 }, + { -96.9073195827836, 32.84533166789337 }, + { -96.9072824639789, 32.84539385307118 }, + { -96.90723443684865, 32.84549894034094 }, + { -96.90713155058394, 32.845570960754024 }, + { -96.90702084565024, 32.84563794599061 }, + { -96.9067482915646, 32.846564928343774 }, + { -96.90658305728276, 32.846808940736665 }, + { -96.90970849593005, 32.84280201671265 }, + { -96.90805395125876, 32.84264819030043 }, + { -96.90782578893621, 32.84251649621324 }, + { -96.90765104983556, 32.84237896970015 }, + { -96.90749277157009, 32.84223664656806 }, + { -96.90732799202755, 32.842079563171204 }, + { -96.90717674658113, 32.84191675639983 }, + { -96.90704206968707, 32.84175826366903 }, + { -96.90691499740588, 32.84158912290766 }, + { -96.90679856537491, 32.841391372546596 }, + { -96.90669280511362, 32.84118105150652 }, + { -96.9066938088171, 32.84036519919393 }, + { -96.90667954376929, 32.839590855466795 }, + { -96.90660899268886, 32.8393190606389 }, + { -96.9065682357046, 32.83905085547807 }, + { -96.90669333896665, 32.83843228118014 }, + { -96.90683531022513, 32.83766337935074 }, + { -96.9058493401867, 32.844535191894416 }, + { -96.90579009265895, 32.8437347614251 }, + { -96.90571275338846, 32.84314413061774 }, + { -96.90568335053055, 32.8427243426205 }, + { -96.90568992027882, 32.84235744093508 }, + { -96.90571448391348, 32.84205446940046 }, + { -96.90577507033056, 32.84175047218241 }, + { -96.90588970181872, 32.841432493758276 }, + { -96.90607139760981, 32.84110657890376 }, + { -96.90924427251379, 32.83993677269292 }, + { -96.90932412469623, 32.839844120427756 }, + { -96.90941109238025, 32.839765667714516 }, + { -96.9094761887729, 32.83972646038609 }, + { -96.90949143753902, 32.83965354450662 }, + { -96.9097457551243, 32.840379966340876 }, + { -96.90982829424877, 32.84071377238854 }, + { -96.90982905264774, 32.840783009315054 }, + { -96.90978899510543, 32.840982723954376 }, + { -96.9096541510239, 32.84115196330865 }, + { -96.9094755291208, 32.84129677452172 }, + { -96.90927714334165, 32.84137120486853 }, + { -96.9091700108835, 32.84129430174039 }, + { -96.90876609180499, 32.84145311262188 }, + { -96.90733733141684, 32.8423466850824 }, + { -96.90684390666796, 32.8425400667973 }, + { -96.90667976396954, 32.842509305466734 }, + { -96.90602783642515, 32.84283044883231 }, + { -96.9062802255586, 32.84267354468892 }, + { -96.90637988293022, 32.84244464082416 }, + { -96.90679282812866, 32.84197678503523 }, + { -96.90742804933328, 32.84135502510003 }, + { -96.9093096344923, 32.83982640878158 }, + { -96.90957633247281, 32.839448983750096 }, + { -96.90978728295761, 32.83902479767383 }, + { -96.90989146055402, 32.8387198981243 }, + { -96.90999388620241, 32.838305332592356 }, + { -96.91004354245484, 32.83786414846141 }, + { -96.910066394355, 32.83761939299617 }, + { -96.91007046155549, 32.8373271133384 }, + { -96.90994976743183, 32.83670035648537 }, + { -96.90993922639949, 32.83636716925954 }, + { -96.90995885480693, 32.83608559832425 }, + { -96.90996464686559, 32.835791690151794 }, + { -96.90999659070908, 32.83546449101174 }, + { -96.91004166908012, 32.83512104695675 }, + { -96.91011885853125, 32.83482440380857 }, + { -96.91020213813562, 32.8346086071453 }, + { -96.91034250605827, 32.834331702289695 }, + { -96.91052593504799, 32.834065734289304 }, + { -96.91068039981232, 32.83385874790652 }, + { -96.91087788560367, 32.83365678760608 }, + { -96.90463542953506, 32.83292489754698 }, + { -96.90449088650593, 32.83277112153798 }, + { -96.90358630936747, 32.83250350307465 }, + { -96.90479021695444, 32.83637208523084 }, + { -96.90470246266294, 32.836230910887174 }, + { -96.91202899207734, 32.8322360224265 }, + { -96.91154896132278, 32.83209846174101 }, + { -96.90859475679387, 32.83192227047514 }, + { -96.90833234079028, 32.83179248980202 }, + { -96.90687768221198, 32.83172416046592 }, + { -96.90658876308076, 32.83159332277057 }, + { -96.90636454683248, 32.8314500165631 }, + { -96.90946092710227, 32.83205428121599 }, + { -96.90977902424702, 32.83184115564981 }, + { -96.91015472158404, 32.83165067826693 }, + { -96.91052898267965, 32.83150288697713 }, + { -96.9112247739368, 32.83137981917697 }, + { -96.91265206016878, 32.83127151173788 }, + { -96.91343580663084, 32.831154000995156 }, + { -96.90537803849678, 32.83031332274312 }, + { -96.90544558911877, 32.830192512197605 }, + { -96.90543348303062, 32.83008060402599 }, + { -96.90534168189792, 32.82995963230952 }, + { -96.90527614279657, 32.829857630539124 }, + { -96.90439482744506, 32.82973863160623 }, + { -96.90338069160265, 32.829630667791726 }, + { -96.90837537183168, 32.83367677072692 }, + { -96.90864247408625, 32.83358197150447 }, + { -96.9092286278605, 32.833477300486074 }, + { -96.90975778683836, 32.833386787416 }, + { -96.91018890496235, 32.8333244613814 }, + { -96.91048593761097, 32.833276350803445 }, + { -96.91077383813835, 32.833248483428406 }, + { -96.91108555891405, 32.83325388631932 }, + { -96.911656037352, 32.83353258584669 }, + { -96.91194125915716, 32.833565607685266 }, + { -96.91260613703052, 32.83397497679704 }, + { -96.91287366825213, 32.834003717435394 }, + { -96.90947673989618, 32.83473585312356 }, + { -96.9096893960709, 32.83452340666632 }, + { -96.91001755333926, 32.83389040012315 }, + { -96.91028013353335, 32.833324854811124 }, + { -96.91050608380527, 32.83286479129949 }, + { -96.91070135504911, 32.83246622940192 }, + { -96.91089890631574, 32.831888188170744 }, + { -96.91108067612203, 32.83131268588944 }, + { -96.91122561582051, 32.83063574006906 }, + { -96.91134268123285, 32.82960936744224 }, + { -96.91152479752446, 32.828746583956615 }, + { -96.91185890321435, 32.82828840477211 }, + { -96.9121429460756, 32.828246844255986 }, + { -96.91175689101942, 32.828078915978715 }, + { -96.91156667517494, 32.82803463270799 }, + { -96.90799324737107, 32.82799800640653 }, + { -96.91916255109285, 32.82801904822771 }, + { -96.91678353408332, 32.82797076851257 }, + { -96.91450013877316, 32.827920176786314 }, + { -96.91332930884246, 32.827887281755665 }, + { -96.91334197954883, 32.82839409130596 }, + { -96.91356812298534, 32.82773961249966 }, + { -96.91391466280625, 32.8269308515725 }, + { -96.91420753589344, 32.82650381393292 }, + { -96.91447968982843, 32.82624350416024 }, + { -96.915293077956, 32.8247219260031 }, + { -96.91557662496521, 32.82445808237808 }, + { -96.91490527680895, 32.82789997536924 }, + { -96.91639800000002, 32.82790060622736 }, + { -96.91973372334664, 32.82797397536925 }, + { -96.92169538978655, 32.827967082377995 }, + { -96.92194694254597, 32.82797392600294 }, + { -96.91114632526379, 32.82803950416009 }, + { -96.91160348003399, 32.82802081393272 }, + { -96.91221835065487, 32.82799985157229 }, + { -96.91284688086124, 32.82800161249959 }, + { -96.9141260137614, 32.827995091306086 }, + { -96.91517469340017, 32.82800628175562 }, + { -96.91638586344432, 32.82802617678628 }, + { -96.91781138884836, 32.824617768514706 }, + { -96.91789537076589, 32.82489904823005 }, + { -96.91775069192848, 32.82575800640849 }, + { -96.91739830014022, 32.82718763270885 }, + { -96.91752308915034, 32.82744291597945 }, + { -96.91760604628695, 32.82801684425629 }, + { -96.91774511911717, 32.828922404771156 }, + { -96.91790124953417, 32.830010583954504 }, + { -96.91808638066573, 32.831187367439334 }, + { -96.91824645048688, 32.83224474006578 }, + { -96.91834638886598, 32.832817685886084 }, + { -96.91842915877513, 32.83333018816722 }, + { -96.91846471341049, 32.83392022939806 }, + { -96.9184489944202, 32.83446079129487 }, + { -96.91883781971728, 32.83240685481398 }, + { -96.91994337233697, 32.83248340012787 }, + { -96.9217193101751, 32.82915340668562 }, + { -96.92239995502872, 32.82934285314428 }, + { -96.92031602448525, 32.828743717456945 }, + { -96.91975955095923, 32.82879597681961 }, + { -96.92077369032383, 32.83275160768903 }, + { -96.91999391775727, 32.83282958585012 }, + { -96.9203551842353, 32.829339886339575 }, + { -96.92096990474985, 32.82943248344921 }, + { -96.92113979465543, 32.82940235082567 }, + { -96.9208148234328, 32.8294894614045 } +}; + +static const std::vector turf_lineDistance = { + 0.04662454909427901, + 0.0068391514197623125, + 0.09985216775589113, + 0.1114897011108262, + 0.07753561751121403, + 0.173873577625755, + 0.14681366156746842, + 0.06837599656020085, + 0.43073382183261627, + 0.15584656488988174, + 0.06961927450155198, + 0.4176778492245252, + 0.11346010319030365, + 0.4069191477510672, + 0.0912702817480534, + 0.21057858475480964, + 0.9731217926415133, + 0.3421517057131934, + 0.24832380027187234, + 0.345931459929503, + 0.269566962191182, + 0.003792191683109981, + 0.33114550846131696, + 0.05016778892928127, + 0.1104041562314812, + 0.2820059822767088, + 0.2592491201412077, + 0.20301909742424815, + 0.3324775424083908, + 0.016729639837999192, + 0.2542614100810735, + 0.2514112112841453, + 0.13668774071427742, + 0.3699064116077255, + 0.5100540014631532, + 0.27709292806311164, + 0.7806905867697003, + 0.7768297872678829, + 0.11390017527821808, + 0.016262433777165904, + 0.585110693926173, + 0.30913282118994834, + 0.26207433401586333, + 0.32168722472380185, + 0.6017929391701553, + 0.009818685405935522, + 0.3856097243248631, + 0.5966547247562556, + 0.44360635693488976, + 0.5884269163801416, + 0.3863202991166816, + 1.0477201274090444, + 0.0875715634398609, + 0.04985868432498091, + 0.06725351272770712, + 0.08775397845174246, + 0.04290208100438889, + 0.04458140027621235 +}; + +static const std::vector turf_area = { + 0.00001385291315291015, + 0.00038253063343961127, + 0.0004601665282670179, + 0.000025503489092274364, + 0.000595225685418049, + 0.0000274562733203963, + 0.010106783304640774, + 0.001104280981730458, + 0.00007467453869082208, + 0.00026115590644638464, + 0.0004233598602508744, + 0.010634164210050902, + 0.0001950421991985674, + 0.00045308678876159587, + 0.11642042926261091, + 0.0023790522112468416, + 0.008515769374449783, + 0.007485271287917676, + 0.0000991431197074571, + 0.006085169973921119, + 0.004123438747704956, + 0.010246920654428558, + 0.0017456776108892168, + 0.0012524502918429697, + 0.0002985554527856464, + 0.013319422885021871, + 0.03301062700761473, + 0.005952119850683508, + 0.07276896988546715, + 0.044671110786017286, + 0.00008921566232195025, + 0.0011521493461515583, + 0.002598288702578828, + 0.00017084098623693146, + 0.009460656476931052, + 0.008810544363699217, + 0.00009774280666531308, + 0.000016747619827861367, + 0.005292114515450739, + 0.00050350329460907, + 0.0013371547851014247, + 0.017404511466653897 +}; + +static const std::vector turf_along_dist = { + 0.023312274547139505, + 0.0034195757098811562, + 0.049926083877945567, + 0.0557448505554131, + 0.038767808755607015, + 0.0869367888128775, + 0.07340683078373421, + 0.034187998280100426, + 0.21536691091630814, + 0.07792328244494087, + 0.03480963725077599, + 0.2088389246122626, + 0.05673005159515183, + 0.2034595738755336, + 0.0456351408740267, + 0.10528929237740482, + 0.48656089632075666, + 0.1710758528565967, + 0.12416190013593617, + 0.1729657299647515, + 0.134783481095591, + 0.0018960958415549904, + 0.16557275423065848, + 0.025083894464640634, + 0.0552020781157406, + 0.1410029911383544, + 0.12962456007060386, + 0.10150954871212407, + 0.1662387712041954, + 0.008364819918999596, + 0.12713070504053675, + 0.12570560564207264, + 0.06834387035713871, + 0.18495320580386276, + 0.2550270007315766, + 0.13854646403155582, + 0.39034529338485013, + 0.38841489363394144, + 0.05695008763910904, + 0.008131216888582952, + 0.2925553469630865, + 0.15456641059497417, + 0.13103716700793167, + 0.16084361236190092, + 0.30089646958507765, + 0.004909342702967761, + 0.19280486216243156, + 0.2983273623781278, + 0.22180317846744488, + 0.2942134581900708, + 0.1931601495583408, + 0.5238600637045222, + 0.04378578171993045, + 0.024929342162490455, + 0.03362675636385356, + 0.04387698922587123, + 0.021451040502194443, + 0.022290700138106176 +}; + +static const std::vector turf_along = { + { -96.92058074705263, 32.838295790638604 }, + { -96.92038500000224, 32.838300500005154 }, + { -96.92017837949811, 32.837840876146984 }, + { -96.91575185809616, 32.834193910150304 }, + { -96.91550349979606, 32.83693300067325 }, + { -96.91447049297071, 32.838291837950294 }, + { -96.91528382138036, 32.83401702016945 }, + { -96.91074827033752, 32.837025468206356 }, + { -96.91465325332314, 32.83491180557744 }, + { -96.9133254981959, 32.83668815734806 }, + { -96.91774498947463, 32.8373234997933 }, + { -96.91858548156746, 32.8367137123682 }, + { -96.912125005592, 32.837061935308064 }, + { -96.91369409219625, 32.83774164388782 }, + { -96.91639766393256, 32.837037707584614 }, + { -96.91504330753082, 32.83916332521944 }, + { -96.91553969265065, 32.83839013773005 }, + { -96.91430070726516, 32.841204555275795 }, + { -96.91571233411688, 32.837779806500336 }, + { -96.9177544997986, 32.83499800000052 }, + { -96.91166706734954, 32.841629875937976 }, + { -96.9107185000003, 32.84265700000001 }, + { -96.91156370689538, 32.83493146645441 }, + { -96.92040000000762, 32.83366850000004 }, + { -96.91099049992684, 32.83367300138629 }, + { -96.91421262070213, 32.83569407895492 }, + { -96.91178370739728, 32.83716800028003 }, + { -96.91715300456607, 32.83775950369486 }, + { -96.9169767353908, 32.840679780152996 }, + { -96.91422649999899, 32.840554000031844 }, + { -96.91397243336206, 32.841584696234726 }, + { -96.91523149763671, 32.84157652148576 }, + { -96.91515150426642, 32.83399134463128 }, + { -96.9142729703425, 32.83879948063655 }, + { -96.9156392575161, 32.835658314541256 }, + { -96.91543436762962, 32.83921014952233 }, + { -96.91983675113876, 32.84033226350925 }, + { -96.92009207607055, 32.840359219198234 }, + { -96.91380542577559, 32.836522062530115 }, + { -96.91423699999902, 32.840453000030095 }, + { -96.91825697483056, 32.836559607150164 }, + { -96.91973317221228, 32.83716163055561 }, + { -96.91180206194203, 32.83642316543709 }, + { -96.91691026889121, 32.84079238934942 }, + { -96.91491045374221, 32.840000370933915 }, + { -96.9150394999991, 32.83688750001095 }, + { -96.91246302385605, 32.83675801387829 }, + { -96.91819499264128, 32.8367903863771 }, + { -96.91518494007596, 32.83533040554249 }, + { -96.91823942303637, 32.83693311262287 }, + { -96.91246139013954, 32.83690233470654 }, + { -96.91460659817584, 32.83801219845079 }, + { -96.91478449997625, 32.840559500872736 }, + { -96.91711949983032, 32.83722250026362 }, + { -96.91476949994939, 32.83659450051385 }, + { -96.91479349998943, 32.84045600087646 }, + { -96.91475749999225, 32.836883000209426 }, + { -96.91476449999463, 32.83676500022616 } +}; + +static const std::vector turf_lineSlice = { + 0.018676803463204973, + 0.0027403004691342625, + 0.0398660924057463, + 0.04446856326668934, + 0.031068781600570992, + 0.06935062861161241, + 0.05864407770725445, + 0.02738946433808026, + 0.17194340067724667, + 0.062425250268892694, + 0.027897179178562482, + 0.16737293529254177, + 0.04543404915442931, + 0.1626655495109919, + 0.036564726727473594, + 0.08399080442136823, + 0.3888893870284878, + 0.13649098383658884, + 0.09926948806782267, + 0.13795991467073365, + 0.10778696255372587, + 0.0015123931765971925, + 0.1320632508079359, + 0.020007277614733015, + 0.04424096972304277, + 0.11264746401401557, + 0.10385609012115837, + 0.08127435006240627, + 0.13303959829411355, + 0.006704402992819479, + 0.10142852122895935, + 0.10028174144491304, + 0.054573146193032135, + 0.14766725949326992, + 0.20376574794349672, + 0.11058823108803342, + 0.31189004060932496, + 0.3103422255762966, + 0.04563922619445161, + 0.006517162427091035, + 0.23447174477127802, + 0.12386733468968403, + 0.10502105512101395, + 0.12872246637948387, + 0.2400138878407706, + 0.003934657129647942, + 0.1545266007655544, + 0.23909936135356857, + 0.17696967699870225, + 0.23580214129066726, + 0.15480959152734855, + 0.41791703759808313, + 0.035094313148852196, + 0.01997359910854999, + 0.026950444653916276, + 0.03516739283887676, + 0.01719229231376514, + 0.0178652399166501 +}; + +static const std::vector turf_bufferPoint = { + {{ -96.92206300843843, 32.83681412509083 }, { -96.91861893543, 32.83970785132817 }}, + {{ -96.92214300909791, 32.836848125090825 }, { -96.91869893477045, 32.839741851328164 }}, + {{ -96.92214300909791, 32.836848125090825 }, { -96.91869893477045, 32.839741851328164 }}, + {{ -96.92225800913668, 32.836850125090805 }, { -96.91881393473163, 32.83974385132815 }}, + {{ -96.9224060090591, 32.836846125090815 }, { -96.91896193480923, 32.83973985132816 }}, + {{ -96.92254001000953, 32.83689512509079 }, { -96.91909593385867, 32.83978885132814 }}, + {{ -96.92207100931128, 32.83685912509082 }, { -96.91862693455705, 32.83975285132816 }}, + {{ -96.92214300909791, 32.836848125090825 }, { -96.91869893477045, 32.839741851328164 }}, + {{ -96.92159599327083, 32.83603212509119 }, { -96.91815195059978, 32.838925851328526 }}, + {{ -96.92181899724693, 32.836237125091095 }, { -96.9183749466231, 32.83913085132843 }}, + {{ -96.92190200035026, 32.83639712509101 }, { -96.91845794351933, 32.83929085132836 }}, + {{ -96.92201200756558, 32.836769125090846 }, { -96.91856793630295, 32.83966285132819 }}, + {{ -96.92206300843843, 32.83681412509083 }, { -96.91861893543, 32.83970785132817 }}, + {{ -96.91750293916103, 32.83324212509243 }, { -96.91405900471734, 32.83613585132978 }}, + {{ -96.91745693605817, 32.833082125092524 }, { -96.91401300782061, 32.83597585132986 }}, + {{ -96.91745193439041, 32.83299612509255 }, { -96.91400800948864, 32.83588985132989 }}, + {{ -96.91745493134579, 32.832839125092626 }, { -96.91401101253369, 32.835732851329965 }}, + {{ -96.91757491998197, 32.83225312509288 }, { -96.91413102389916, 32.83514685133023 }}, + {{ -96.91681398182763, 32.83544212509144 }, { -96.91336996204461, 32.83833585132879 }}, + {{ -96.91763698353438, 32.83553012509141 }, { -96.91419296033762, 32.83842385132875 }}, + {{ -96.91633899406605, 32.836073125091154 }, { -96.91289494980445, 32.8389668513285 }}, + {{ -96.91622900543202, 32.836659125090904 }, { -96.91278493843684, 32.83955285132824 }}, + {{ -96.91604102399457, 32.83761612509046 }, { -96.91259691987165, 32.840509851327816 }}, + {{ -96.91739291499829, 32.831996125093006 }, { -96.91394902888356, 32.834889851330345 }}, + {{ -96.91714892225083, 32.83237012509284 }, { -96.91370502162997, 32.83526385133019 }}, + {{ -96.91704192508207, 32.83251612509276 }, { -96.91359801879831, 32.8354098513301 }}, + {{ -96.91696892719582, 32.83262512509271 }, { -96.91352501668426, 32.835518851330065 }}, + {{ -96.91678393068645, 32.83280512509264 }, { -96.91334001319315, 32.83569885132999 }}, + {{ -96.91649693584488, 32.833071125092516 }, { -96.91305300803398, 32.83596485132987 }}, + {{ -96.9121219862885, 32.83567212509135 }, { -96.90867795758311, 32.83856585132869 }}, + {{ -96.91228898531874, 32.83562212509136 }, { -96.90884495855302, 32.838515851328715 }}, + {{ -96.91282198283616, 32.83549412509143 }, { -96.90937796103594, 32.83838785132877 }}, + {{ -96.91613197587343, 32.83513512509158 }, { -96.9126879679997, 32.838028851328936 }}, + {{ -96.91594697513642, 32.835097125091615 }, { -96.91250296873679, 32.83799085132895 }}, + {{ -96.91587997426367, 32.83505212509162 }, { -96.91243596960967, 32.837945851328975 }}, + {{ -96.91582897329396, 32.835002125091656 }, { -96.91238497057954, 32.837895851328994 }}, + {{ -96.91579697220784, 32.83494612509167 }, { -96.91235297166578, 32.837839851329015 }}, + {{ -96.915780970249, 32.83484512509171 }, { -96.9123369736249, 32.83773885132906 }}, + {{ -96.91581296600165, 32.83462612509182 }, { -96.91236897787286, 32.83751985132916 }}, + {{ -96.91637094362142, 32.833472125092335 }, { -96.91292700025633, 32.83636585132968 }}, + {{ -96.91655893745447, 32.833154125092484 }, { -96.91311500642416, 32.83604785132982 }}, + {{ -96.9165799371054, 32.83313612509249 }, { -96.91313600677327, 32.83602985132983 }}, + {{ -96.91663393671753, 32.8331161250925 }, { -96.91319000716118, 32.836009851329834 }}, + {{ -96.91668993667875, 32.83311412509251 }, { -96.91324600719997, 32.83600785132984 }}, + {{ -96.91675193685329, 32.83312312509249 }, { -96.91330800702542, 32.83601685132983 }}, + {{ -96.91680293727993, 32.833145125092486 }, { -96.91335900659871, 32.83603885132984 }}, + {{ -96.91683793797807, 32.833181125092466 }, { -96.91339400590047, 32.83607485132981 }}, + {{ -96.91685593881195, 32.83322412509245 }, { -96.91341200506646, 32.836117851329796 }}, + {{ -96.91686193972342, 32.83327112509243 }, { -96.91341800415486, 32.836164851329784 }}, + {{ -96.91684594117791, 32.8333461250924 }, { -96.9134020027002, 32.836239851329736 }}, + {{ -96.9166529517084, 32.83388912509215 }, { -96.91320899216815, 32.83678285132949 }}, + {{ -96.9166099550635, 32.834062125092075 }, { -96.91316598881261, 32.836955851329414 }}, + {{ -96.91653196285982, 32.83446412509189 }, { -96.91308798101515, 32.83735785132922 }}, + {{ -96.91587697928689, 32.83531112509151 }, { -96.9124329645857, 32.83820485132886 }}, + {{ -96.91511297810382, 32.83525012509153 }, { -96.91166896576897, 32.83814385132888 }}, + {{ -96.91471297705651, 32.83519612509156 }, { -96.91126896681644, 32.83808985132891 }}, + {{ -96.91456897600918, 32.83514212509158 }, { -96.9111249678639, 32.838035851328925 }}, + {{ -96.91448297443822, 32.83506112509162 }, { -96.9110389694351, 32.83795485132896 }}, + {{ -96.91429497298361, 32.834986125091646 }, { -96.9108509708899, 32.83787985132899 }}, + {{ -96.91909898943054, 32.83583412509127 }, { -96.91565495444063, 32.83872785132861 }}, + {{ -96.91931099012878, 32.835870125091255 }, { -96.9158669537423, 32.83876385132859 }}, + {{ -96.91983899055546, 32.83589212509124 }, { -96.91639495331553, 32.838785851328595 }}, + {{ -96.92253697610617, 32.83514712509158 }, { -96.91909296776691, 32.83804085132892 }}, + {{ -96.9214539770565, 32.83519612509156 }, { -96.91800996681643, 32.83808985132891 }}, + {{ -96.9206969782008, 32.835255125091535 }, { -96.91725296567199, 32.83814885132888 }}, + {{ -96.91979897872444, 32.83528212509151 }, { -96.91635496514823, 32.838175851328856 }}, + {{ -96.9183829785499, 32.83527312509153 }, { -96.91493896532283, 32.838166851328864 }}, + {{ -96.91807997959722, 32.8353271250915 }, { -96.91463596427536, 32.83822085132884 }}, + {{ -96.91326498252585, 32.835478125091434 }, { -96.9098209613463, 32.83837185132878 }}, + {{ -96.91352798409686, 32.83555912509139 }, { -96.91008395977508, 32.83845285132874 }}, + {{ -96.91371598452355, 32.83558112509138 }, { -96.91027195934834, 32.83847485132872 }}, + {{ -96.9140479861915, 32.83566712509135 }, { -96.91060395768008, 32.838560851328694 }}, + {{ -96.9142739878983, 32.8357551250913 }, { -96.91082995597309, 32.83864885132865 }}, + {{ -96.91437798960509, 32.835843125091266 }, { -96.91093395426604, 32.83873685132861 }}, + {{ -96.91637897736683, 32.835212125091545 }, { -96.91293496650609, 32.83810585132889 }}, + {{ -96.91623999410484, 32.836075125091156 }, { -96.91279594976567, 32.8389688513285 }}, + {{ -96.91619399703357, 32.836226125091095 }, { -96.9127499468365, 32.83911985132844 }}, + {{ -96.91610499895377, 32.83632512509105 }, { -96.91266094491604, 32.8392188513284 }}, + {{ -96.91600600062179, 32.836411125091 }, { -96.91256194324774, 32.83930485132834 }}, + {{ -96.91587500153341, 32.83645812509098 }, { -96.912430942336, 32.839351851328324 }}, + {{ -96.91574600166918, 32.83646512509099 }, { -96.91230194220022, 32.83935885132834 }}, + {{ -96.91564700114552, 32.83643812509099 }, { -96.912202942724, 32.83933185132834 }}, + {{ -96.91552600003993, 32.83638112509103 }, { -96.91208194382972, 32.83927485132837 }}, + {{ -96.91539999811975, 32.83628212509107 }, { -96.91195594575017, 32.839175851328406 }}, + {{ -96.91511299327082, 32.83603212509119 }, { -96.91166895059978, 32.838925851328526 }}, + {{ -96.91499999034212, 32.83588112509125 }, { -96.91155595352893, 32.838774851328594 }}, + {{ -96.91497398859653, 32.835791125091305 }, { -96.91152995527474, 32.83868485132864 }}, + {{ -96.91498398685096, 32.835701125091326 }, { -96.91153995702057, 32.838594851328665 }}, + {{ -96.9150269854933, 32.83563112509135 }, { -96.91158295837842, 32.8385248513287 }}, + {{ -96.91509198444597, 32.83557712509138 }, { -96.91164795942592, 32.83847085132873 }}, + {{ -96.91520198405807, 32.8355571250914 }, { -96.91175795981388, 32.83845085132874 }}, + {{ -96.91620498165308, 32.835433125091456 }, { -96.9127609622192, 32.8383268513288 }}, + {{ -96.91763698353438, 32.83553012509141 }, { -96.91419296033762, 32.83842385132875 }}, + {{ -96.91790498405807, 32.8355571250914 }, { -96.91446095981388, 32.83845085132874 }}, + {{ -96.91814698479507, 32.83559512509137 }, { -96.91470295907675, 32.83848885132871 }}, + {{ -96.91858398716128, 32.83571712509132 }, { -96.9151399567102, 32.83861085132867 }}, + {{ -96.91690504414862, 32.83865512509 }, { -96.91346089971469, 32.84154885132734 }}, + {{ -96.91687304079277, 32.838482125090074 }, { -96.91342890307102, 32.84137585132741 }}, + {{ -96.91683503322767, 32.838092125090256 }, { -96.91339091063722, 32.840985851327595 }}, + {{ -96.91673002224884, 32.837526125090506 }, { -96.91328592161763, 32.840419851327844 }}, + {{ -96.91667101753538, 32.837283125090615 }, { -96.91322692633173, 32.84017685132796 }}, + {{ -96.9166310128996, 32.83704412509072 }, { -96.9131869309682, 32.83993785132806 }}, + {{ -96.91655600791474, 32.83678712509084 }, { -96.91311193595378, 32.83968085132818 }}, + {{ -96.9164500940436, 32.84122712508884 }, { -96.91300584981255, 32.844120851326174 }}, + {{ -96.91649409202596, 32.84112312508889 }, { -96.9130498518305, 32.84401685132623 }}, + {{ -96.91659408634168, 32.840830125089006 }, { -96.91314985751559, 32.84372385132636 }}, + {{ -96.91669607978447, 32.84049212508917 }, { -96.91325186407373, 32.843385851326516 }}, + {{ -96.91673607410037, 32.8401991250893 }, { -96.91329186975865, 32.84309285132664 }}, + {{ -96.91678706894014, 32.83993312508942 }, { -96.91334287491964, 32.84282685132677 }}, + {{ -96.91683506137448, 32.8395431250896 }, { -96.91339088248637, 32.842436851326944 }}, + {{ -96.91689704839683, 32.8388741250899 }, { -96.91345289546588, 32.84176785132724 }}, + {{ -96.9169150388142, 32.838380125090126 }, { -96.91347090504988, 32.84127385132747 }}, + {{ -96.91692602946458, 32.83789812509034 }, { -96.91348191440086, 32.84079185132768 }}, + {{ -96.91697202364541, 32.83759812509048 }, { -96.91352792022083, 32.84049185132781 }}, + {{ -96.9170470180979, 32.83731212509061 }, { -96.91360292576915, 32.84020585132795 }}, + {{ -96.91720501237589, 32.83701712509073 }, { -96.91376093149196, 32.83991085132807 }}, + {{ -96.91738000795353, 32.836789125090846 }, { -96.91393593591498, 32.83968285132819 }}, + {{ -96.91762900227046, 32.836496125090974 }, { -96.91418494159886, 32.83938985132831 }}, + {{ -96.91793199773183, 32.836262125091075 }, { -96.91448794613815, 32.839155851328414 }}, + {{ -96.91808199585044, 32.83616512509112 }, { -96.9146379480198, 32.83905885132847 }}, + {{ -96.91830999330962, 32.83603412509118 }, { -96.914865950561, 32.83892785132853 }}, + {{ -96.91866999026453, 32.83587712509125 }, { -96.91522595360651, 32.8387708513286 }}, + {{ -96.91903198762677, 32.83574112509132 }, { -96.91558795624465, 32.838634851328656 }}, + {{ -96.9197479858812, 32.83565112509135 }, { -96.91630395799046, 32.8385448513287 }}, + {{ -96.92056898514417, 32.83561312509138 }, { -96.91712495872758, 32.838506851328724 }}, + {{ -96.92111298475629, 32.83559312509138 }, { -96.91766895911556, 32.838486851328724 }}, + {{ -96.92156898417443, 32.83556312509139 }, { -96.9181249596975, 32.838456851328736 }}, + {{ -96.92189598322406, 32.83551412509142 }, { -96.91845196064797, 32.83840785132876 }}, + {{ -96.91632503611791, 32.838241125090185 }, { -96.91288090774655, 32.84113485132754 }}, + {{ -96.91605706325616, 32.83964012508955 }, { -96.91261288060439, 32.8425338513269 }}, + {{ -96.91582107895027, 32.84044912508918 }, { -96.91237686490803, 32.843342851326526 }}, + {{ -96.91554509216175, 32.841130125088874 }, { -96.91210085169466, 32.84402385132622 }}, + {{ -96.9154920940436, 32.84122712508884 }, { -96.91204784981255, 32.844120851326174 }}, + {{ -96.91763698353438, 32.83553012509141 }, { -96.91419296033762, 32.83842385132875 }}, + {{ -96.91782698479507, 32.83559512509137 }, { -96.91438295907676, 32.83848885132871 }}, + {{ -96.91790498623033, 32.83566912509136 }, { -96.91446095764131, 32.83856285132869 }}, + {{ -96.91793998716128, 32.83571712509132 }, { -96.9144959567102, 32.83861085132867 }}, + {{ -96.91795398824742, 32.83577312509131 }, { -96.91450995562391, 32.83866685132865 }}, + {{ -96.91794498995421, 32.83586112509127 }, { -96.91450095391689, 32.8387548513286 }}, + {{ -96.91792399086579, 32.835908125091244 }, { -96.91447995300516, 32.83880185132858 }}, + {{ -96.91772799476429, 32.83610912509115 }, { -96.9142839491061, 32.83900285132848 }}, + {{ -96.91743599908953, 32.83633212509104 }, { -96.91399194478024, 32.839225851328386 }}, + {{ -96.91731000027268, 32.83639312509102 }, { -96.91386594359693, 32.83928685132837 }}, + {{ -96.91714100087395, 32.836424125091014 }, { -96.91369694299557, 32.839317851328346 }}, + {{ -96.91701500079637, 32.83642012509102 }, { -96.91357094307315, 32.839313851328356 }}, + {{ -96.91686200003993, 32.83638112509103 }, { -96.9134179438297, 32.83927485132837 }}, + {{ -96.91678399899256, 32.836327125091046 }, { -96.91333994487724, 32.839220851328385 }}, + {{ -96.91673599821672, 32.83628712509106 }, { -96.91329194565319, 32.83918085132841 }}, + {{ -96.91664699588924, 32.83616712509113 }, { -96.91320294798099, 32.83906085132847 }}, + {{ -96.91660999396908, 32.83606812509117 }, { -96.91316594990144, 32.838961851328506 }}, + {{ -96.91655298974086, 32.83585012509127 }, { -96.91310895413027, 32.8387438513286 }}, + {{ -96.91946491499829, 32.831996125093006 }, { -96.91602102888356, 32.834889851330345 }}, + {{ -96.91948797531097, 32.8351061250916 }, { -96.9160439685622, 32.83799985132893 }}, + {{ -96.9141210940436, 32.84122712508884 }, { -96.91067684981255, 32.844120851326174 }}, + {{ -96.91320906880432, 32.83992612508941 }, { -96.90976487505543, 32.842819851326766 }}, + {{ -96.9121220693087, 32.83995212508941 }, { -96.90867787455099, 32.84284585132676 }}, + {{ -96.91244209338399, 32.84119312508886 }, { -96.90899785047226, 32.844086851326196 }}, + {{ -96.9124390940436, 32.84122712508884 }, { -96.90899484981256, 32.844120851326174 }}, + {{ -96.91330591499829, 32.831996125093006 }, { -96.90986202888357, 32.834889851330345 }}, + {{ -96.9133029196717, 32.83223712509289 }, { -96.90985902420945, 32.835130851330234 }}, + {{ -96.91328095044784, 32.83382412509218 }, { -96.90983699342891, 32.83671785132952 }}, + {{ -96.9132759727315, 32.834973125091665 }, { -96.90983197114207, 32.837866851329 }}, + {{ -96.92212491499828, 32.831996125093006 }, { -96.91868102888355, 32.834889851330345 }}, + {{ -96.92211892374401, 32.8324471250928 }, { -96.91867502013655, 32.83534085133014 }}, + {{ -96.91212191924508, 32.8322151250929 }, { -96.90867802463615, 32.83510885133024 }}, + {{ -96.9133029196717, 32.83223712509289 }, { -96.90985902420945, 32.835130851330234 }}, + {{ -96.91649693584488, 32.833071125092516 }, { -96.91305300803398, 32.83596485132987 }}, + {{ -96.9162339459292, 32.833591125092276 }, { -96.91278999794821, 32.83648485132962 }}, + {{ -96.91604895366716, 32.8339901250921 }, { -96.91260499020915, 32.836883851329446 }}, + {{ -96.9157379672235, 32.8346891250918 }, { -96.91229397665084, 32.83758285132914 }}, + {{ -96.91569496865867, 32.834763125091754 }, { -96.91225097521547, 32.83765685132909 }}, + {{ -96.91562797001627, 32.83483312509173 }, { -96.91218397385767, 32.83772685132906 }}, + {{ -96.91549097154844, 32.83491212509169 }, { -96.9120469723253, 32.83780585132903 }}, + {{ -96.9153969720333, 32.83493712509168 }, { -96.91195297184036, 32.837830851329024 }}, + {{ -96.91492497321636, 32.834998125091644 }, { -96.91148097065712, 32.83789185132899 }}, + {{ -96.91212198846075, 32.835784125091294 }, { -96.90867795541052, 32.83867785132863 }}, + {{ -96.9124169878983, 32.8357551250913 }, { -96.90897295597308, 32.83864885132865 }}, + {{ -96.91274698751039, 32.835735125091325 }, { -96.90930295636103, 32.83862885132866 }}, + {{ -96.91307698723887, 32.835721125091325 }, { -96.90963295663262, 32.838614851328664 }}, + {{ -96.91366998723886, 32.835721125091325 }, { -96.9102259566326, 32.838614851328664 }}, + {{ -96.91411298828622, 32.835775125091295 }, { -96.91066895558512, 32.83866885132864 }}, + {{ -96.91437798960509, 32.835843125091266 }, { -96.91093395426604, 32.83873685132861 }}, + {{ -96.91479399406605, 32.836073125091154 }, { -96.91134994980445, 32.8389668513285 }}, + {{ -96.91791100686734, 32.83673312509086 }, { -96.91446693700134, 32.83962685132821 }}, + {{ -96.91983899055546, 32.83589212509124 }, { -96.91639495331553, 32.838785851328595 }}, + {{ -96.9198870768939, 32.84034312508924 }, { -96.9164428669647, 32.84323685132657 }}, + {{ -96.91905106133568, 32.8395411250896 }, { -96.91560688252517, 32.84243485132695 }}, + {{ -96.91874205582643, 32.83925712508973 }, { -96.91529788803521, 32.84215085132708 }}, + {{ -96.91853305355679, 32.839140125089784 }, { -96.91508889030517, 32.84203385132712 }}, + {{ -96.9183830522377, 32.83907212508981 }, { -96.91493889162446, 32.84196585132715 }}, + {{ -96.9180900509768, 32.83900712508984 }, { -96.91464589288553, 32.84190085132718 }}, + {{ -96.91788605066641, 32.83899112508985 }, { -96.91444189319596, 32.84188485132719 }}, + {{ -96.9169850510544, 32.83901112508983 }, { -96.91354089280793, 32.84190485132717 }}, + {{ -96.91585905289725, 32.83910612508979 }, { -96.91241489096481, 32.84199985132713 }}, + {{ -96.91603805293605, 32.839108125089794 }, { -96.91259389092602, 32.84200185132714 }}, + {{ -96.915872050938, 32.83900512508984 }, { -96.91242789292434, 32.84189885132718 }}, + {{ -96.91577606509908, 32.83973512508951 }, { -96.91233187876121, 32.84262885132685 }}, + {{ -96.91569807266481, 32.84012512508934 }, { -96.91225387119442, 32.843018851326676 }}, + {{ -96.91556408191846, 32.84060212508912 }, { -96.91211986193943, 32.84349585132646 }}, + {{ -96.9154130889995, 32.84096712508895 }, { -96.91196885485739, 32.8438608513263 }}, + {{ -96.9152910940436, 32.84122712508884 }, { -96.91184684981255, 32.844120851326174 }}, + {{ -96.9166410940436, 32.84122712508884 }, { -96.91319684981255, 32.844120851326174 }}, + {{ -96.9166420939854, 32.841224125088836 }, { -96.91319784987074, 32.844117851326175 }}, + {{ -96.91682208428526, 32.84072412508907 }, { -96.9133778595723, 32.84361785132641 }}, + {{ -96.91696607165603, 32.84007312508936 }, { -96.91352187220332, 32.8429668513267 }}, + {{ -96.91704406116109, 32.839532125089605 }, { -96.91359988269978, 32.84242585132694 }}, + {{ -96.91709805320761, 32.83912212508979 }, { -96.91365389065439, 32.84201585132713 }}, + {{ -96.91710605097681, 32.83900712508984 }, { -96.91366189288554, 32.84190085132718 }}, + {{ -96.91719591499829, 32.831996125093006 }, { -96.91375202888356, 32.834889851330345 }}, + {{ -96.91710091928385, 32.83221712509291 }, { -96.91365702459737, 32.83511085133024 }}, + {{ -96.91703592137819, 32.832325125092865 }, { -96.91359202250273, 32.835218851330204 }}, + {{ -96.91698292287138, 32.83240212509282 }, { -96.91353902100933, 32.83529585133016 }}, + {{ -96.91680292740914, 32.832636125092705 }, { -96.91335901647092, 32.83552985133006 }}, + {{ -96.91649693584488, 32.833071125092516 }, { -96.91305300803398, 32.83596485132987 }}, + {{ -96.91479399406605, 32.836073125091154 }, { -96.91134994980445, 32.8389668513285 }}, + {{ -96.91491699577286, 32.836161125091124 }, { -96.9114729480974, 32.83905485132847 }}, + {{ -96.91539700341482, 32.836555125090946 }, { -96.91195294045433, 32.83944885132829 }}, + {{ -96.91541600358939, 32.83656412509095 }, { -96.91197194027974, 32.839457851328284 }}, + {{ -96.91568200805051, 32.83679412509083 }, { -96.91223793581798, 32.83968785132818 }}, + {{ -96.9157760100677, 32.83689812509079 }, { -96.91233193380047, 32.83979185132812 }}, + {{ -96.91588301320994, 32.837060125090716 }, { -96.91243893065779, 32.83995385132806 }}, + {{ -96.91597701722505, 32.83726712509063 }, { -96.91253292664213, 32.840160851327965 }}, + {{ -96.9160140206389, 32.83744312509054 }, { -96.91256992322779, 32.84033685132788 }}, + {{ -96.91604102399457, 32.83761612509046 }, { -96.91259691987165, 32.840509851327816 }}, + {{ -96.91590404407103, 32.83865112509 }, { -96.9124598997923, 32.84154485132734 }}, + {{ -96.915872050938, 32.83900512508984 }, { -96.91242789292434, 32.84189885132718 }}, + {{ -96.91912797538855, 32.835110125091596 }, { -96.91568396848461, 32.838003851328935 }}, + {{ -96.91760197461278, 32.83507012509161 }, { -96.91415796926053, 32.837963851328965 }}, + {{ -96.91749997430246, 32.83505412509162 }, { -96.91405596957087, 32.83794785132897 }}, + {{ -96.91744897391457, 32.835034125091624 }, { -96.91400496995882, 32.83792785132896 }}, + {{ -96.91741197346849, 32.83501112509164 }, { -96.91396797040495, 32.837904851328986 }}, + {{ -96.91736597277028, 32.83497512509165 }, { -96.91392197110326, 32.83786885132899 }}, + {{ -96.91733097199452, 32.83493512509168 }, { -96.91388697187915, 32.83782885132902 }}, + {{ -96.91730997133509, 32.8349011250917 }, { -96.91386597253866, 32.837794851329036 }}, + {{ -96.91729397050113, 32.834858125091706 }, { -96.91384997337273, 32.83775185132905 }}, + {{ -96.91728596914353, 32.83478812509174 }, { -96.91384197473054, 32.83768185132909 }}, + {{ -96.9172859675726, 32.83470712509179 }, { -96.91384197630171, 32.837600851329114 }}, + {{ -96.91738995428774, 32.83402212509209 }, { -96.91394598958846, 32.83691585132943 }}, + {{ -96.91747594183725, 32.833380125092376 }, { -96.91403200204074, 32.83627385132972 }}, + {{ -96.91750293916103, 32.83324212509243 }, { -96.91405900471734, 32.83613585132978 }}, + {{ -96.9175569365818, 32.833109125092506 }, { -96.91411300729696, 32.83600285132985 }}, + {{ -96.9177739272346, 32.83262712509272 }, { -96.91433001664547, 32.83552085133007 }}, + {{ -96.91800491499829, 32.831996125093006 }, { -96.91456102888357, 32.834889851330345 }}, + {{ -96.91710605097681, 32.83900712508984 }, { -96.91366189288554, 32.84190085132718 }}, + {{ -96.91713003817407, 32.83834712509014 }, { -96.9136859056901, 32.84124085132747 }}, + {{ -96.91713302946458, 32.83789812509034 }, { -96.91368891440086, 32.84079185132768 }}, + {{ -96.91718102409155, 32.83762112509046 }, { -96.91373691977464, 32.840514851327804 }}, + {{ -96.91726201976603, 32.83739812509056 }, { -96.91381792410078, 32.8402918513279 }}, + {{ -96.91735801670134, 32.83724012509064 }, { -96.91391392716592, 32.840133851327984 }}, + {{ -96.91748701363667, 32.83708212509072 }, { -96.91404293023102, 32.83997585132805 }}, + {{ -96.91766701031987, 32.836911125090786 }, { -96.91422293354829, 32.83980485132813 }}, + {{ -96.91791100686734, 32.83673312509086 }, { -96.91446693700134, 32.83962685132821 }}, + {{ -96.9211429870643, 32.83571212509132 }, { -96.91769895680719, 32.838605851328666 }}, + {{ -96.92127898816983, 32.83576912509129 }, { -96.9178349557015, 32.83866285132864 }}, + {{ -96.92141898956629, 32.83584112509127 }, { -96.91797495430484, 32.83873485132861 }}, + {{ -96.92153399173858, 32.83595312509122 }, { -96.91808995213223, 32.83884685132857 }}, + {{ -96.92159599327083, 32.83603212509119 }, { -96.91815195059978, 32.838925851328526 }}, + {{ -96.92189401031986, 32.836911125090786 }, { -96.91844993354829, 32.83980485132813 }}, + {{ -96.92201701976603, 32.83739812509056 }, { -96.91857292410077, 32.8402918513279 }}, + {{ -96.92205502409155, 32.83762112509046 }, { -96.91861091977464, 32.840514851327804 }}, + {{ -96.92204903095816, 32.83797512509031 }, { -96.91860491290703, 32.84086885132764 }}, + {{ -96.92194503724296, 32.83829912509015 }, { -96.91850090662132, 32.84119285132749 }}, + {{ -96.92179404306233, 32.83859912509002 }, { -96.91834990080113, 32.84149285132736 }}, + {{ -96.9216200475239, 32.83882912508992 }, { -96.91817589633894, 32.84172285132727 }}, + {{ -96.92153404905635, 32.83890812508988 }, { -96.91808989480624, 32.84180185132723 }}, + {{ -96.92114805516688, 32.839223125089745 }, { -96.91770388869487, 32.842116851327084 }}, + {{ -96.91973407553594, 32.84027312508926 }, { -96.91628986832288, 32.84316685132661 }}, + {{ -96.91925208232587, 32.8406231250891 }, { -96.91580786153199, 32.84351685132645 }}, + {{ -96.91909608477026, 32.84074912508905 }, { -96.91565185908723, 32.8436428513264 }}, + {{ -96.9184490940436, 32.84122712508884 }, { -96.91500484981255, 32.844120851326174 }}, + {{ -96.91870309404361, 32.84122712508884 }, { -96.91525884981256, 32.844120851326174 }}, + {{ -96.91880109264676, 32.841155125088875 }, { -96.9153568512096, 32.84404885132621 }}, + {{ -96.91920908661328, 32.84084412508902 }, { -96.91576485724394, 32.84373785132635 }}, + {{ -96.91983607759231, 32.840379125089214 }, { -96.91639186626621, 32.84327285132656 }}, + {{ -96.92170605097681, 32.83900712508984 }, { -96.91826189288554, 32.84190085132718 }}, + {{ -96.92195804668978, 32.83878612508993 }, { -96.91851389717318, 32.84167985132728 }}, + {{ -96.9221510414911, 32.838518125090054 }, { -96.91870690237259, 32.84141185132741 }}, + {{ -96.92223403860082, 32.83836912509012 }, { -96.91878990526328, 32.841262851327464 }}, + {{ -96.92231203357683, 32.838110125090246 }, { -96.918867910288, 32.841003851327585 }}, + {{ -96.92233402802918, 32.83782412509037 }, { -96.91888991583646, 32.84071785132771 }}, + {{ -96.92232602628341, 32.837734125090414 }, { -96.91888191758245, 32.84062785132776 }}, + {{ -96.92229602360662, 32.83759612509048 }, { -96.91885192025964, 32.84048985132782 }}, + {{ -96.92213801443192, 32.83712312509069 }, { -96.91869392943565, 32.84001685132803 }}, + {{ -96.92208701094057, 32.83694312509076 }, { -96.91864293292753, 32.83983685132811 }}, + {{ -96.92206300843843, 32.83681412509083 }, { -96.91861893543, 32.83970785132817 }}, + {{ -96.92202200568417, 32.836672125090885 }, { -96.91857793818467, 32.83956585132823 }}, + {{ -96.92200400227046, 32.836496125090974 }, { -96.91855994159886, 32.83938985132831 }}, + {{ -96.92199599852705, 32.836303125091064 }, { -96.9185519453428, 32.8391968513284 }}, + {{ -96.92201699567589, 32.83615612509112 }, { -96.91857294819438, 32.83904985132846 }}, + {{ -96.92204099437637, 32.836089125091156 }, { -96.91859694949407, 32.8389828513285 }}, + {{ -96.92211899187434, 32.83596012509121 }, { -96.91867495199645, 32.83885385132855 }}, + {{ -96.9222369895663, 32.83584112509127 }, { -96.91879295430485, 32.83873485132861 }}, + {{ -96.92232298838319, 32.8357801250913 }, { -96.91887895548814, 32.83867385132864 }}, + {{ -96.92244898727766, 32.83572312509131 }, { -96.91900495659381, 32.83861685132866 }}, + {{ -96.91613197587343, 32.83513512509158 }, { -96.9126879679997, 32.838028851328936 }}, + {{ -96.91590997566009, 32.8351241250916 }, { -96.91246596821304, 32.83801785132894 }}, + {{ -96.91492497321636, 32.834998125091644 }, { -96.91148097065712, 32.83789185132899 }}, + {{ -96.9160460509768, 32.83900712508984 }, { -96.91260189288553, 32.84190085132718 }}, + {{ -96.915872050938, 32.83900512508984 }, { -96.91242789292434, 32.84189885132718 }}, + {{ -96.92310897612555, 32.83514812509159 }, { -96.91966496774752, 32.838041851328924 }}, + {{ -96.92253697610617, 32.83514712509158 }, { -96.91909296776691, 32.83804085132892 }}, + {{ -96.91948797531097, 32.8351061250916 }, { -96.9160439685622, 32.83799985132893 }}, + {{ -96.91912797538855, 32.835110125091596 }, { -96.91568396848461, 32.838003851328935 }}, + {{ -96.91757297662981, 32.83517412509158 }, { -96.91412896724319, 32.838067851328915 }}, + {{ -96.91718097662981, 32.83517412509158 }, { -96.91373696724318, 32.838067851328915 }}, + {{ -96.91685097635828, 32.835160125091576 }, { -96.91340696751473, 32.838053851328915 }}, + {{ -96.91983899055546, 32.83589212509124 }, { -96.91639495331553, 32.838785851328595 }}, + {{ -96.92004598886807, 32.83580512509128 }, { -96.91660195500317, 32.838698851328616 }}, + {{ -96.92030798758799, 32.835739125091315 }, { -96.91686395628346, 32.83863285132866 }}, + {{ -96.9205659871031, 32.83571412509132 }, { -96.91712195676841, 32.83860785132867 }}, + {{ -96.9211429870643, 32.83571212509132 }, { -96.91769895680719, 32.838605851328666 }}, + {{ -96.92244898727766, 32.83572312509131 }, { -96.91900495659381, 32.83861685132866 }}, + {{ -96.92310898727766, 32.83572312509131 }, { -96.9196649565938, 32.83861685132866 }}, + {{ -96.91492497321636, 32.834998125091644 }, { -96.91148097065712, 32.83789185132899 }}, + {{ -96.9148639730806, 32.83499112509165 }, { -96.9114199707929, 32.83788485132899 }}, + {{ -96.9147209730806, 32.83499112509165 }, { -96.91127697079291, 32.83788485132899 }}, + {{ -96.91449597286727, 32.834980125091654 }, { -96.91105197100629, 32.837873851329 }}, + {{ -96.91429497298361, 32.834986125091646 }, { -96.9108509708899, 32.83787985132899 }}, + {{ -96.9132759727315, 32.834973125091665 }, { -96.90983197114207, 32.837866851329 }}, + {{ -96.91212197265392, 32.83496912509166 }, { -96.90867797121963, 32.83786285132901 }}, + {{ -96.91697505311063, 32.839117125089786 }, { -96.9135308907514, 32.842010851327124 }}, + {{ -96.91709805320761, 32.83912212508979 }, { -96.91365389065439, 32.84201585132713 }}, + {{ -96.91753805307184, 32.83911512508979 }, { -96.9140938907902, 32.84200885132714 }}, + {{ -96.91791905316883, 32.83912012508979 }, { -96.91447489069319, 32.842013851327124 }}, + {{ -96.91820005377018, 32.83915112508977 }, { -96.91475589009175, 32.842044851327124 }}, + {{ -96.91834505460432, 32.839194125089755 }, { -96.91490088925751, 32.84208785132709 }}, + {{ -96.91847905578763, 32.83925512508973 }, { -96.91503488807402, 32.842148851327075 }}, + {{ -96.9186350575723, 32.83934712508969 }, { -96.91519088628907, 32.84224085132703 }}, + {{ -96.9190480646141, 32.83971012508952 }, { -96.91560387924628, 32.84260385132687 }}, + {{ -96.91917406684502, 32.83982512508947 }, { -96.91572987701505, 32.8427188513268 }}, + {{ -96.91967807633132, 32.84031412508924 }, { -96.91623386752738, 32.843207851326596 }}, + {{ -96.91978307838768, 32.8404201250892 }, { -96.9163388654707, 32.84331385132655 }}, + {{ -96.9162220940436, 32.84122712508884 }, { -96.91277784981256, 32.844120851326174 }}, + {{ -96.91626909132755, 32.841087125088904 }, { -96.912824852529, 32.84398085132624 }}, + {{ -96.91643008040526, 32.84052412508915 }, { -96.91298586345285, 32.84341785132649 }}, + {{ -96.91652407074426, 32.84002612508938 }, { -96.91307987311522, 32.84291985132673 }}, + {{ -96.91658006308158, 32.83963112508957 }, { -96.91313588077902, 32.8425248513269 }}, + {{ -96.91660405656359, 32.839295125089706 }, { -96.91315988729797, 32.842188851327045 }}, + {{ -96.9166290465152, 32.83877712508994 }, { -96.91318489734778, 32.84167085132729 }}, + {{ -96.91663703646707, 32.83825912509018 }, { -96.91319290739735, 32.84115285132752 }}, + {{ -96.91660702440191, 32.83763712509046 }, { -96.91316291946426, 32.840530851327806 }}, + {{ -96.9165480055096, 32.83666312509089 }, { -96.91310393835924, 32.83955685132825 }}, + {{ -96.91655298974086, 32.83585012509127 }, { -96.91310895413027, 32.8387438513286 }}, + {{ -96.91670898176945, 32.83543912509145 }, { -96.91326496210282, 32.838332851328786 }}, + {{ -96.91681398182763, 32.83544212509144 }, { -96.91336996204461, 32.83833585132879 }}, + {{ -96.91624797938387, 32.83531612509151 }, { -96.91280396448872, 32.83820985132885 }}, + {{ -96.91587697928689, 32.83531112509151 }, { -96.9124329645857, 32.83820485132886 }}, + {{ -96.9121219792869, 32.83531112509151 }, { -96.9086779645857, 32.83820485132886 }}, + {{ -96.92310898035362, 32.83536612509148 }, { -96.91966496351884, 32.83825985132882 }}, + {{ -96.9205469800239, 32.83534912509149 }, { -96.9171029638486, 32.838242851328836 }}, + {{ -96.91807997959722, 32.8353271250915 }, { -96.91463596427536, 32.83822085132884 }}, + {{ -96.91672497946146, 32.83532012509149 }, { -96.91328096441114, 32.838213851328845 }}, + {{ -96.91655298974086, 32.83585012509127 }, { -96.91310895413027, 32.8387438513286 }}, + {{ -96.91659397744439, 32.835216125091556 }, { -96.91314996642849, 32.838109851328895 }}, + {{ -96.91675496210344, 32.834425125091904 }, { -96.91331098177162, 32.83731885132925 }}, + {{ -96.91686195411319, 32.8340131250921 }, { -96.91341798976302, 32.83690685132943 }}, + {{ -96.91694794930363, 32.83376512509221 }, { -96.9135039945733, 32.836658851329545 }}, + {{ -96.91757491998197, 32.83225312509288 }, { -96.91413102389916, 32.83514685133023 }}, + {{ -96.91767191499827, 32.831996125093006 }, { -96.91422802888354, 32.834889851330345 }}, + {{ -96.91681398182763, 32.83544212509144 }, { -96.91336996204461, 32.83833585132879 }}, + {{ -96.91811998186644, 32.835444125091456 }, { -96.91467596200583, 32.838337851328795 }}, + {{ -96.92126898326285, 32.835516125091424 }, { -96.91782496060918, 32.83840985132876 }}, + {{ -96.92304398304951, 32.835505125091416 }, { -96.91959996082257, 32.838398851328755 }}, + {{ -96.92310898304952, 32.835505125091416 }, { -96.91966496082256, 32.838398851328755 }}, + {{ -96.91212198413564, 32.835561125091395 }, { -96.90867795973627, 32.838454851328734 }}, + {{ -96.91239298353439, 32.83553012509141 }, { -96.90894896033761, 32.83842385132875 }}, + {{ -96.91282198283616, 32.83549412509143 }, { -96.90937796103594, 32.83838785132877 }}, + {{ -96.91326498252585, 32.835478125091434 }, { -96.9098209613463, 32.83837185132878 }}, + {{ -96.9143589820022, 32.83545112509144 }, { -96.91091496187005, 32.838344851328785 }}, + {{ -96.91522298176945, 32.83543912509145 }, { -96.91177896210282, 32.838332851328786 }}, + {{ -96.91624998165308, 32.835433125091456 }, { -96.9128059622192, 32.8383268513288 }}, + {{ -96.91749191499827, 32.831996125093006 }, { -96.91404802888356, 32.834889851330345 }}, + {{ -96.91739291984624, 32.83224612509289 }, { -96.91394902403492, 32.835139851330226 }}, + {{ -96.91706593584487, 32.833071125092516 }, { -96.91362200803398, 32.83596485132987 }}, + {{ -96.91653196285982, 32.83446412509189 }, { -96.91308798101515, 32.83735785132922 }}, + {{ -96.91647596704895, 32.83468012509178 }, { -96.91303197682542, 32.83757385132914 }}, + {{ -96.91637897736683, 32.835212125091545 }, { -96.91293496650609, 32.83810585132889 }}, + {{ -96.91633899406605, 32.836073125091154 }, { -96.91289494980445, 32.8389668513285 }}, + {{ -96.91631701425736, 32.837114125090686 }, { -96.91287292961024, 32.84000785132803 }}, + {{ -96.91632503611791, 32.838241125090185 }, { -96.91288090774655, 32.84113485132754 }}, + {{ -96.91630905561304, 32.83924612508974 }, { -96.91286488824862, 32.84213985132708 }}, + {{ -96.91623406566167, 32.8397641250895 }, { -96.91278987819858, 32.84265785132684 }}, + {{ -96.91614307448836, 32.84021912508929 }, { -96.9126988693706, 32.84311285132663 }}, + {{ -96.91600608477026, 32.84074912508905 }, { -96.91256185908722, 32.8436428513264 }}, + {{ -96.91581909404358, 32.84122712508884 }, { -96.91237484981255, 32.844120851326174 }}, + {{ -96.91603805293605, 32.839108125089794 }, { -96.91259389092602, 32.84200185132714 }}, + {{ -96.91697505311063, 32.839117125089786 }, { -96.9135308907514, 32.842010851327124 }}, + {{ -96.91858398716128, 32.83571712509132 }, { -96.9151399567102, 32.83861085132867 }}, + {{ -96.91909898943054, 32.83583412509127 }, { -96.91565495444063, 32.83872785132861 }}, + {{ -96.91685097635828, 32.835160125091576 }, { -96.91340696751473, 32.838053851328915 }}, + {{ -96.91613197587343, 32.83513512509158 }, { -96.9126879679997, 32.838028851328936 }}, + {{ -96.9169850510544, 32.83901112508983 }, { -96.91354089280793, 32.84190485132717 }}, + {{ -96.9160460509768, 32.83900712508984 }, { -96.91260189288553, 32.84190085132718 }}, + {{ -96.91624998165308, 32.835433125091456 }, { -96.9128059622192, 32.8383268513288 }}, + {{ -96.91670898176945, 32.83543912509145 }, { -96.91326496210282, 32.838332851328786 }}, + {{ -96.91672497946146, 32.83532012509149 }, { -96.91328096441114, 32.838213851328845 }}, + {{ -96.91624797938387, 32.83531612509151 }, { -96.91280396448872, 32.83820985132885 }} +}; diff --git a/third_party/geometry.hpp-0.9.2/.gitignore b/third_party/geometry.hpp-0.9.2/.gitignore new file mode 100644 index 00000000000..204298f2389 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/.gitignore @@ -0,0 +1,2 @@ +mason_packages +test diff --git a/third_party/geometry.hpp-0.9.2/.gitmodules b/third_party/geometry.hpp-0.9.2/.gitmodules new file mode 100644 index 00000000000..44fba9a2fd4 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/.gitmodules @@ -0,0 +1,3 @@ +[submodule ".mason"] + path = .mason + url = https://github.com/mapbox/mason.git diff --git a/third_party/geometry.hpp-0.9.2/.travis.yml b/third_party/geometry.hpp-0.9.2/.travis.yml new file mode 100644 index 00000000000..a567bdc5d8d --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/.travis.yml @@ -0,0 +1,50 @@ +language: generic +sudo: false + +matrix: + include: + - os: linux + env: CXX=g++-4.9 + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'g++-4.9' ] + - os: linux + env: CXX=g++-5 + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'g++-5' ] + - os: linux + env: CXX=g++-6 + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'g++-6' ] + - os: linux + env: CXX=clang++-3.8 + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'libstdc++-4.9-dev' ] + before_script: + - git submodule update --init + - .mason/mason install clang++ 3.8.1 + - export PATH=$(.mason/mason prefix clang++ 3.8.1)/bin:$PATH + - os: linux + env: CXX=clang++-3.9 + addons: + apt: + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'libstdc++-4.9-dev' ] + before_script: + - git submodule update --init + - .mason/mason install clang++ 3.9.1 + - export PATH=$(.mason/mason prefix clang++ 3.9.1)/bin:$PATH + - os: osx + osx_image: xcode7.3 + +cache: apt + +script: + - make test diff --git a/third_party/geometry.hpp-0.9.2/LICENSE b/third_party/geometry.hpp-0.9.2/LICENSE new file mode 100644 index 00000000000..8f6a86bd07e --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2016, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/third_party/geometry.hpp-0.9.2/Makefile b/third_party/geometry.hpp-0.9.2/Makefile new file mode 100644 index 00000000000..ec95379ec22 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/Makefile @@ -0,0 +1,19 @@ +CXXFLAGS += -I include -std=c++14 -DDEBUG -O0 -Wall -Wextra -Werror +MASON ?= .mason/mason + +VARIANT = 1.1.4 + +default: test + +$(MASON): + git submodule update --init + +mason_packages/headers/variant/$(VARIANT): + $(MASON) install variant $(VARIANT) + +test: tests/* include/mapbox/geometry/* mason_packages/headers/variant/$(VARIANT) Makefile + $(CXX) tests/*.cpp $(CXXFLAGS) `$(MASON) cflags variant $(VARIANT)` -o test + ./test + +clean: + rm -f test diff --git a/third_party/geometry.hpp-0.9.2/README.md b/third_party/geometry.hpp-0.9.2/README.md new file mode 100644 index 00000000000..972614a3bc9 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/README.md @@ -0,0 +1,83 @@ +# geometry.hpp + +Provides header-only, generic C++ interfaces for geometry types, geometry collections, and features. + + - `mapbox::geometry::point` + - `mapbox::geometry::multi_point` + - `mapbox::geometry::line_string` + - `mapbox::geometry::multi_line_string` + - `mapbox::geometry::polygon` + - `mapbox::geometry::multi_polygon` + - `mapbox::geometry::geometry_collection` + - `mapbox::geometry::feature` (experimental) + +### Design + +These types are designed to be easy to parse and serialize to [GeoJSON](http://geojson.org/). + +They should also be a robust and high performance container for data processing and conversion. + + +### Goals + + - Header-only + - Fast compile + - c++11/c++14 compatibility + - No external dependencies for usage of core types (point, line_string, etc) + - Minimal dependencies for usage of enclosing `geometry` type (`mapbox::variant`) + - Easily [adaptable to `boost::geometry`](http://www.boost.org/doc/libs/1_56_0/libs/geometry/doc/html/geometry/examples/example__adapting_a_legacy_geometry_object_model.html) + + +### Usage + +Using a single type directly (requires no external dependencies): + +```cpp +#include +#include + +using mapbox::geometry::point; + +int main() { + point pt(1.0,0.0); + std::clog << "x: " << pt.x << " y: " << pt.y << "\n"; +} +``` + +Creating a geometry collection (depends on https://github.com/mapbox/variant): + +```cpp +#include +#include +#include + +using mapbox::geometry::geometry_collection; +using mapbox::geometry::geometry; +using mapbox::geometry::point; + +using point_type = point; + +struct printer +{ + printer() {} + + void operator()(point_type const& pt) const + { + std::clog << "x: " << pt.x << " y: " << pt.y << "\n"; + } + + template + void operator()(T const& g) const + { + std::clog << "encountered non-point geometry\n"; + } +}; + +int main() { + geometry_collection gc; + gc.emplace_back(point_type(1.0,0.0)); + geometry const& geom = gc.at(0); + printer visitor; + mapbox::util::apply_visitor(visitor,geom); +} +``` diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry.hpp new file mode 100644 index 00000000000..e232453179e --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/box.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/box.hpp new file mode 100644 index 00000000000..bf81b703ecd --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/box.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace mapbox { +namespace geometry { + +template +struct box +{ + using point_type = point; + + constexpr box(point_type const& min_, point_type const& max_) + : min(min_), max(max_) + {} + + point_type min; + point_type max; +}; + +template +constexpr bool operator==(box const& lhs, box const& rhs) +{ + return lhs.min == rhs.min && lhs.max == rhs.max; +} + +template +constexpr bool operator!=(box const& lhs, box const& rhs) +{ + return lhs.min != rhs.min || lhs.max != rhs.max; +} + +} // namespace geometry +} // namespace mapbox diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/envelope.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/envelope.hpp new file mode 100644 index 00000000000..86035839858 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/envelope.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +#include + +namespace mapbox { +namespace geometry { + +template +box envelope(G const& geometry) +{ + using limits = std::numeric_limits; + + T min_t = limits::has_infinity ? -limits::infinity() : limits::min(); + T max_t = limits::has_infinity ? limits::infinity() : limits::max(); + + point min(max_t, max_t); + point max(min_t, min_t); + + for_each_point(geometry, [&] (point const& point) { + if (min.x > point.x) min.x = point.x; + if (min.y > point.y) min.y = point.y; + if (max.x < point.x) max.x = point.x; + if (max.y < point.y) max.y = point.y; + }); + + return box(min, max); +} + +} // namespace geometry +} // namespace mapbox diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/feature.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/feature.hpp new file mode 100644 index 00000000000..91354442eed --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/feature.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include + +namespace mapbox { +namespace geometry { + +struct value; + +struct null_value_t +{ + constexpr null_value_t() {} + constexpr null_value_t(std::nullptr_t) {} +}; + +constexpr bool operator==(const null_value_t&, const null_value_t&) { return true; } +constexpr bool operator!=(const null_value_t&, const null_value_t&) { return false; } +constexpr bool operator<(const null_value_t&, const null_value_t&) { return false; } + +constexpr null_value_t null_value = null_value_t(); + +// Multiple numeric types (uint64_t, int64_t, double) are present in order to support +// the widest possible range of JSON numbers, which do not have a maximum range. +// Implementations that produce `value`s should use that order for type preference, +// using uint64_t for positive integers, int64_t for negative integers, and double +// for non-integers and integers outside the range of 64 bits. +using value_base = mapbox::util::variant>, + mapbox::util::recursive_wrapper>>; + +struct value : value_base +{ + using value_base::value_base; +}; + +using property_map = std::unordered_map; + +// The same considerations and requirement for numeric types apply as for `value_base`. +using identifier = mapbox::util::variant; + +template +struct feature +{ + using coordinate_type = T; + using geometry_type = mapbox::geometry::geometry; // Fully qualified to avoid GCC -fpermissive error. + + geometry_type geometry; + property_map properties {}; + mapbox::util::optional id {}; + + // GCC 4.9 does not support C++14 aggregates with non-static data member + // initializers. + feature(geometry_type geometry_, + property_map properties_ = property_map {}, + mapbox::util::optional id_ = mapbox::util::optional {}) + : geometry(std::move(geometry_)), + properties(std::move(properties_)), + id(std::move(id_)) {} +}; + +template +constexpr bool operator==(feature const& lhs, feature const& rhs) +{ + return lhs.id == rhs.id && lhs.geometry == rhs.geometry && lhs.properties == rhs.properties; +} + +template +constexpr bool operator!=(feature const& lhs, feature const& rhs) +{ + return !(lhs == rhs); +} + +template class Cont = std::vector> +struct feature_collection : Cont> +{ + using coordinate_type = T; + using feature_type = feature; + using container_type = Cont; + using container_type::container_type; +}; + +} // namespace geometry +} // namespace mapbox diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/for_each_point.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/for_each_point.hpp new file mode 100644 index 00000000000..44d6e77bd0f --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/for_each_point.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include + +namespace mapbox { +namespace geometry { + +template +auto for_each_point(Point&& point, F&& f) + -> decltype(point.x, point.y, void()) +{ + f(std::forward(point)); +} + +template +auto for_each_point(Container&& container, F&& f) + -> decltype(container.begin(), container.end(), void()); + +template +void for_each_point(mapbox::util::variant const& geom, F&& f) +{ + mapbox::util::variant::visit(geom, [&] (auto const& g) { + for_each_point(g, f); + }); +} + +template +void for_each_point(mapbox::util::variant & geom, F&& f) +{ + mapbox::util::variant::visit(geom, [&] (auto & g) { + for_each_point(g, f); + }); +} + +template +auto for_each_point(Container&& container, F&& f) + -> decltype(container.begin(), container.end(), void()) +{ + for (auto& e: container) { + for_each_point(e, f); + } +} + +} // namespace geometry +} // namespace mapbox diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/geometry.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/geometry.hpp new file mode 100644 index 00000000000..a9d072be7e2 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/geometry.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +// stl +#include + +namespace mapbox { +namespace geometry { + +template class Cont = std::vector> +struct geometry_collection; + +template +using geometry_base = mapbox::util::variant, + line_string, + polygon, + multi_point, + multi_line_string, + multi_polygon, + geometry_collection>; + +template +struct geometry : geometry_base +{ + using coordinate_type = T; + using geometry_base::geometry_base; + + /* + * The default constructor would create a point geometry with default-constructed coordinates; + * i.e. (0, 0). Since this is not particularly useful, and could hide bugs, it is disabled. + */ + geometry() = delete; +}; + +template class Cont> +struct geometry_collection : Cont> +{ + using coordinate_type = T; + using geometry_type = geometry; + using container_type = Cont; + + geometry_collection() = default; + geometry_collection(geometry_collection const&) = default; + geometry_collection(geometry_collection &&) = default; + geometry_collection(std::initializer_list && args) + : container_type(std::forward>(args)) {}; +}; + +} // namespace geometry +} // namespace mapbox diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/line_string.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/line_string.hpp new file mode 100644 index 00000000000..6d811ce2210 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/line_string.hpp @@ -0,0 +1,21 @@ +#pragma once + +// mapbox +#include +// stl +#include + +namespace mapbox { +namespace geometry { + +template class Cont = std::vector> +struct line_string : Cont > +{ + using coordinate_type = T; + using point_type = point; + using container_type = Cont; + using container_type::container_type; +}; + +} // namespace geometry +} // namespace mapbox diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/multi_line_string.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/multi_line_string.hpp new file mode 100644 index 00000000000..07a7a1d609d --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/multi_line_string.hpp @@ -0,0 +1,21 @@ +#pragma once + +// mapbox +#include +// stl +#include + +namespace mapbox { +namespace geometry { + +template class Cont = std::vector> +struct multi_line_string : Cont> +{ + using coordinate_type = T; + using line_string_type = line_string; + using container_type = Cont; + using container_type::container_type; +}; + +} // namespace geometry +} // namespace mapbox diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/multi_point.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/multi_point.hpp new file mode 100644 index 00000000000..a3c73cff912 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/multi_point.hpp @@ -0,0 +1,21 @@ +#pragma once + +// mapbox +#include +// stl +#include + +namespace mapbox { +namespace geometry { + +template class Cont = std::vector> +struct multi_point : Cont> +{ + using coordinate_type = T; + using point_type = point; + using container_type = Cont; + using container_type::container_type; +}; + +} // namespace geometry +} // namespace mapbox diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/multi_polygon.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/multi_polygon.hpp new file mode 100644 index 00000000000..ad230a04c85 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/multi_polygon.hpp @@ -0,0 +1,21 @@ +#pragma once + +// mapbox +#include +// stl +#include + +namespace mapbox { +namespace geometry { + +template class Cont = std::vector> +struct multi_polygon : Cont> +{ + using coordinate_type = T; + using polygon_type = polygon; + using container_type = Cont; + using container_type::container_type; +}; + +} // namespace geometry +} // namespace mapbox diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/point.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/point.hpp new file mode 100644 index 00000000000..0cba4996e7f --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/point.hpp @@ -0,0 +1,35 @@ +#pragma once + +namespace mapbox { +namespace geometry { + +template +struct point +{ + using coordinate_type = T; + + constexpr point() + : x(), y() + {} + constexpr point(T x_, T y_) + : x(x_), y(y_) + {} + + T x; + T y; +}; + +template +constexpr bool operator==(point const& lhs, point const& rhs) +{ + return lhs.x == rhs.x && lhs.y == rhs.y; +} + +template +constexpr bool operator!=(point const& lhs, point const& rhs) +{ + return !(lhs == rhs); +} + +} // namespace geometry +} // namespace mapbox diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/point_arithmetic.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/point_arithmetic.hpp new file mode 100644 index 00000000000..0c4c63278a5 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/point_arithmetic.hpp @@ -0,0 +1,119 @@ +#pragma once + +namespace mapbox { +namespace geometry { + +template +point operator+(point const& lhs, point const& rhs) +{ + return point(lhs.x + rhs.x, lhs.y + rhs.y); +} + +template +point operator+(point const& lhs, T const& rhs) +{ + return point(lhs.x + rhs, lhs.y + rhs); +} + +template +point operator-(point const& lhs, point const& rhs) +{ + return point(lhs.x - rhs.x, lhs.y - rhs.y); +} + +template +point operator-(point const& lhs, T const& rhs) +{ + return point(lhs.x - rhs, lhs.y - rhs); +} + +template +point operator*(point const& lhs, point const& rhs) +{ + return point(lhs.x * rhs.x, lhs.y * rhs.y); +} + +template +point operator*(point const& lhs, T const& rhs) +{ + return point(lhs.x * rhs, lhs.y * rhs); +} + +template +point operator/(point const& lhs, point const& rhs) +{ + return point(lhs.x / rhs.x, lhs.y / rhs.y); +} + +template +point operator/(point const& lhs, T const& rhs) +{ + return point(lhs.x / rhs, lhs.y / rhs); +} + +template +point& operator+=(point& lhs, point const& rhs) +{ + lhs.x += rhs.x; + lhs.y += rhs.y; + return lhs; +} + +template +point& operator+=(point& lhs, T const& rhs) +{ + lhs.x += rhs; + lhs.y += rhs; + return lhs; +} + +template +point& operator-=(point& lhs, point const& rhs) +{ + lhs.x -= rhs.x; + lhs.y -= rhs.y; + return lhs; +} + +template +point& operator-=(point& lhs, T const& rhs) +{ + lhs.x -= rhs; + lhs.y -= rhs; + return lhs; +} + +template +point& operator*=(point& lhs, point const& rhs) +{ + lhs.x *= rhs.x; + lhs.y *= rhs.y; + return lhs; +} + +template +point& operator*=(point& lhs, T const& rhs) +{ + lhs.x *= rhs; + lhs.y *= rhs; + return lhs; +} + +template +point& operator/=(point& lhs, point const& rhs) +{ + lhs.x /= rhs.x; + lhs.y /= rhs.y; + return lhs; +} + +template +point& operator/=(point& lhs, T const& rhs) +{ + lhs.x /= rhs; + lhs.y /= rhs; + return lhs; +} + +} // namespace geometry +} // namespace mapbox diff --git a/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/polygon.hpp b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/polygon.hpp new file mode 100644 index 00000000000..99a66aa1259 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/include/mapbox/geometry/polygon.hpp @@ -0,0 +1,31 @@ +#pragma once + +// mapbox +#include + +// stl +#include + +namespace mapbox { +namespace geometry { + +template class Cont = std::vector> +struct linear_ring : Cont> +{ + using coordinate_type = T; + using point_type = point; + using container_type = Cont; + using container_type::container_type; +}; + +template class Cont = std::vector> +struct polygon : Cont> +{ + using coordinate_type = T; + using linear_ring_type = linear_ring; + using container_type = Cont; + using container_type::container_type; +}; + +} // namespace geometry +} // namespace mapbox diff --git a/third_party/geometry.hpp-0.9.2/tests/collection.cpp b/third_party/geometry.hpp-0.9.2/tests/collection.cpp new file mode 100644 index 00000000000..a3105a4e883 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/tests/collection.cpp @@ -0,0 +1,5 @@ +#include + +void test() { + mapbox::geometry::geometry_collection gc; +} diff --git a/third_party/geometry.hpp-0.9.2/tests/test.cpp b/third_party/geometry.hpp-0.9.2/tests/test.cpp new file mode 100644 index 00000000000..fcbde4fdcb5 --- /dev/null +++ b/third_party/geometry.hpp-0.9.2/tests/test.cpp @@ -0,0 +1,260 @@ +#include + +#include + +using namespace mapbox::geometry; + +static void testPoint() { + point p1; + assert(int(p1.x) == 0); + assert(int(p1.y) == 0); + + point p2(2, 3); + point p3(4, 6); + + assert((p2 + p3) == point(6, 9)); + assert((p2 + 1u) == point(3, 4)); + assert((p3 - p2) == point(2, 3)); + assert((p3 - 1u) == point(3, 5)); + assert((p3 * p2) == point(8, 18)); + assert((p2 * 2u) == point(4, 6)); + assert((p3 / p2) == point(2, 2)); + assert((p3 / 2u) == point(2, 3)); + + { point p(2, 3); assert((p += p3) == point(6, 9)); } + { point p(2, 3); assert((p += 1u) == point(3, 4)); } + { point p(4, 6); assert((p -= p2) == point(2, 3)); } + { point p(4, 6); assert((p -= 1u) == point(3, 5)); } + { point p(4, 6); assert((p *= p2) == point(8, 18)); } + { point p(2, 3); assert((p *= 2u) == point(4, 6)); } + { point p(4, 6); assert((p /= p2) == point(2, 2)); } + { point p(4, 6); assert((p /= 2u) == point(2, 3)); } +} + +static void testMultiPoint() { + multi_point mp1; + assert(mp1.size() == 0); + + multi_point mp2(10); + assert(mp2.size() == 10); + + assert(mp1 == mp1); + assert(!(mp1 != mp1)); + assert(mp1 != mp2); +} + +static void testLineString() { + line_string ls1; + assert(ls1.size() == 0); + + line_string ls2(10); + assert(ls2.size() == 10); + + assert(ls1 == ls1); + assert(!(ls1 != ls1)); + assert(ls1 != ls2); +} + +static void testMultiLineString() { + multi_line_string mls1; + assert(mls1.size() == 0); + + multi_line_string mls2(10); + assert(mls2.size() == 10); + + assert(mls1 == mls1); + assert(!(mls1 != mls1)); + assert(mls1 != mls2); +} + +static void testPolygon() { + polygon pg1; + assert(pg1.size() == 0); + + polygon pg2({{{0, 1}}}); + assert(pg2.size() == 1); + assert(pg2[0].size() == 1); + assert(pg2[0][0] == point(0, 1)); + + assert(pg1 == pg1); + assert(!(pg1 != pg1)); + assert(pg1 != pg2); +} + +static void testMultiPolygon() { + multi_polygon mpg1; + assert(mpg1.size() == 0); + + multi_polygon mpg2(10); + assert(mpg2.size() == 10); + + assert(mpg1 == mpg1); + assert(!(mpg1 != mpg1)); + assert(mpg1 != mpg2); +} + +static void testGeometry() { + geometry pg { point() }; + assert(pg.is>()); + + geometry lsg { line_string() }; + assert(lsg.is>()); + + geometry pgg { polygon() }; + assert(pgg.is>()); + + geometry mpg { multi_point() }; + assert(mpg.is>()); + + geometry mlsg { multi_line_string() }; + assert(mlsg.is>()); + + geometry mpgg { multi_polygon() }; + assert(mpgg.is>()); + + geometry gcg { geometry_collection() }; + assert(gcg.is>()); + + assert(pg == pg); + assert(!(pg != pg)); + assert(pg != lsg); +} + +static void testGeometryCollection() { + geometry_collection gc1; + assert(gc1.size() == 0); + + assert(gc1 == gc1); + assert(!(gc1 != gc1)); +} + +static void testFeature() { + feature pf { point() }; + assert(pf.geometry.is>()); + assert(pf.properties.size() == 0); + + auto &p = pf.properties; + + p["bool"] = true; + p["string"] = std::string("foo"); + p["double"] = 2.5; + p["uint"] = uint64_t(10); + p["int"] = int64_t(-10); + p["null"] = null_value; + + assert(p["bool"].is()); + assert(p["bool"] == true); + assert(p["string"].is()); + assert(p["string"] == std::string("foo")); + assert(p["double"].is()); + assert(p["double"] == 2.5); + assert(p["uint"].is()); + assert(p["uint"] == uint64_t(10)); + assert(p["int"].is()); + assert(p["int"] == int64_t(-10)); + assert(p["null"].is()); + assert(p["null"] == null_value); + + p["null"] = null_value_t{}; + assert(p["null"].is()); + assert(p["null"] == null_value); + + assert(p == p); + assert(!(p != p)); + + assert(pf == pf); + assert(!(pf != pf)); + + assert(p.size() == 6); + + feature id1 { point() }; + id1.id = { uint64_t(1) }; + + feature id2 { point() }; + id1.id = { uint64_t(2) }; + + assert(id1 == id1); + assert(id1 != id2); +} + +static void testFeatureCollection() { + feature_collection fc1; + assert(fc1.size() == 0); + + assert(fc1 == fc1); + assert(!(fc1 != fc1)); +} + +struct point_counter { + std::size_t count = 0; + template + void operator()(Point const&) { count++; }; +}; + +static void testForEachPoint() { + auto count_points = [] (auto const& g) { + point_counter counter; + for_each_point(g, counter); + return counter.count; + }; + + assert(count_points(point()) == 1); + assert(count_points(line_string({{0, 1}, {2, 3}})) == 2); + assert(count_points(geometry(polygon({{{0, 1}, {2, 3}}}))) == 2); + + auto point_negator = [] (point& p) { p *= -1.0; }; + + point p(1, 2); + for_each_point(p, point_negator); + assert(p == point(-1, -2)); + + line_string ls({{0, 1}, {2, 3}}); + for_each_point(ls, point_negator); + assert(ls == line_string({{0, -1}, {-2, -3}})); + + geometry g(polygon({{{0, 1}, {2, 3}}})); + for_each_point(g, point_negator); + assert(g == geometry(polygon({{{0, -1}, {-2, -3}}}))); + + // Custom geometry type + using my_geometry = mapbox::util::variant>; + assert(count_points(my_geometry(point())) == 1); + + // Custom point type + struct my_point { + int16_t x; + int16_t y; + }; + assert(count_points(std::vector({my_point{0, 1}})) == 1); + assert(count_points(mapbox::util::variant(my_point{0, 1})) == 1); +} + +static void testEnvelope() { + assert(envelope(point(0, 0)) == box({0, 0}, {0, 0})); + assert(envelope(line_string({{0, 1}, {2, 3}})) == box({0, 1}, {2, 3})); + assert(envelope(polygon({{{0, 1}, {2, 3}}})) == box({0, 1}, {2, 3})); + + assert(envelope(multi_point({{0, 0}})) == box({0, 0}, {0, 0})); + assert(envelope(multi_line_string({{{0, 1}, {2, 3}}})) == box({0, 1}, {2, 3})); + assert(envelope(multi_polygon({{{{0, 1}, {2, 3}}}})) == box({0, 1}, {2, 3})); + + assert(envelope(geometry(point(0, 0))) == box({0, 0}, {0, 0})); + assert(envelope(geometry_collection({point(0, 0)})) == box({0, 0}, {0, 0})); +} + +int main() { + testPoint(); + testMultiPoint(); + testLineString(); + testMultiLineString(); + testPolygon(); + testMultiPolygon(); + testGeometry(); + testGeometryCollection(); + testFeature(); + testFeatureCollection(); + + testForEachPoint(); + testEnvelope(); + return 0; +} diff --git a/third_party/libosmium/.clang-tidy b/third_party/libosmium/.clang-tidy new file mode 100644 index 00000000000..9f3240b8700 --- /dev/null +++ b/third_party/libosmium/.clang-tidy @@ -0,0 +1,90 @@ +--- +Checks: '*,-android-cloexec-*,-cert-dcl21-cpp,-cert-err58-cpp,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-vararg,-fuchsia-*,-google-runtime-references,-hicpp-invalid-access-moved,-hicpp-no-array-decay,-hicpp-no-assembler,-hicpp-vararg,-misc-macro-parentheses,-misc-unused-parameters,-modernize-make-unique,-modernize-raw-string-literal,-readability-avoid-const-params-in-decls,-readability-implicit-bool-cast,-readability-implicit-bool-conversion' +# +# For a list of check options, see: +# http://clang.llvm.org/extra/clang-tidy/checks/list.html +# +# Disabled checks: +# +# android-cloexec-* +# O_CLOEXEC isn't available on Windows making this non-portable. +# +# cert-dcl21-cpp +# It is unclear whether this is still a good recommendation in modern C++. +# +# cert-err58-cpp +# Used in several singelton factory functions. I don't know of a better +# way to do this and it is rather unlikely that this will be a problem. +# +# cppcoreguidelines-owning-memory +# Don't want to add dependency on gsl library. +# +# cppcoreguidelines-pro-bounds-array-to-pointer-decay +# Limited use and many false positives including for all asserts. +# +# cppcoreguidelines-pro-bounds-constant-array-index +# Is needed for low-level code. +# +# cppcoreguidelines-pro-bounds-pointer-arithmetic +# This is a low-level library, it needs to do pointer arithmetic. +# +# cppcoreguidelines-pro-type-const-cast +# When you need it, you need it. +# +# cppcoreguidelines-pro-type-reinterpret-cast +# This is a low-level library, it needs to do reinterpret-casts. +# +# cppcoreguidelines-pro-type-static-cast-downcast +# This is needed and totally okay if we are sure about the types. +# +# cppcoreguidelines-pro-type-vararg +# We need some of these functions at least and for some functions it isn't +# even clear that those are vararg functions. +# +# fuchsia-* +# Much too strict. +# +# google-runtime-references +# This is just a matter of preference, and we can't change the interfaces +# now anyways. +# +# hicpp-invalid-access-moved +# Creates false positives. +# +# hicpp-no-array-decay +# Alias for cppcoreguidelines-pro-bounds-array-to-pointer-decay. +# +# hicpp-no-assembler +# Reports are from macros we don't have any control over. +# +# hicpp-vararg +# Too strict, sometimes calling vararg functions is necessary. +# +# misc-macro-parentheses +# False positive in the only place where it reports something and +# disabling locally doesn't work. +# +# misc-unused-parameters +# Can't be fixed, because then Doxygen will complain. (In file +# include/osmium/area/problem_reporter.hpp). +# +# modernize-make-unique +# This is a C++11 library and C++ doesn't have std::make_unique. +# +# modernize-raw-string-literal +# Readability isn't that much better, arguably worse because of the new +# syntax. +# +# readability-avoid-const-params-in-decls +# This is header only library, so the declaration and implementation are +# often the same and we want to have the const in implementations. +# +# readability-implicit-bool-cast +# Old name for readability-implicit-bool-conversion. +# +# readability-implicit-bool-conversion +# I don't think this makes the code more readable. +# +#WarningsAsErrors: '*' +HeaderFilterRegex: '\/include\/osmium\/.*' +... diff --git a/third_party/libosmium/.gitmodules b/third_party/libosmium/.gitmodules new file mode 100644 index 00000000000..b42b21d77cd --- /dev/null +++ b/third_party/libosmium/.gitmodules @@ -0,0 +1,3 @@ +[submodule "test/data-tests/osm-testdata"] + path = test/data-tests/osm-testdata + url = https://github.com/osmcode/osm-testdata diff --git a/third_party/libosmium/.travis.yml b/third_party/libosmium/.travis.yml index 33239630886..bc5615d9b8b 100644 --- a/third_party/libosmium/.travis.yml +++ b/third_party/libosmium/.travis.yml @@ -8,6 +8,10 @@ language: generic sudo: false +dist: trusty + +#----------------------------------------------------------------------------- + cache: directories: - $HOME/.ccache @@ -18,176 +22,195 @@ env: - CCACHE_COMPRESS=1 - CASHER_TIME_OUT=1000 +#----------------------------------------------------------------------------- + +# Save common build configurations as shortcuts, so we can reference them later. +addons_shortcuts: + addons_clang35: &clang35 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] + packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'clang-3.5'] + addons_clang38: &clang38 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] + packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'clang-3.8'] + addons_clang39: &clang39 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] + packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'clang-3.9'] + addons_clang40: &clang40 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest', 'llvm-toolchain-trusty-4.0' ] + packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'clang-4.0'] + addons_clang50: &clang50 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest', 'llvm-toolchain-trusty-5.0' ] + packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'clang-5.0', 'g++-6', 'gcc-6'] + addons_gcc48: &gcc48 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] + packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'g++-4.8', 'gcc-4.8' ] + addons_gcc49: &gcc49 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] + packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'g++-4.9', 'gcc-4.9' ] + addons_gcc5: &gcc5 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] + packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'g++-5', 'gcc-5' ] + addons_gcc6: &gcc6 + apt: + sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] + packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'g++-6', 'gcc-6' ] + +#----------------------------------------------------------------------------- + matrix: include: # 1/ Linux Clang Builds - os: linux compiler: linux-clang35-release - addons: - apt: - sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.5', 'cmake', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.5' BUILD_TYPE='Release' + env: CC='clang-3.5' CXX='clang++-3.5' BUILD_TYPE='Release' + addons: *clang35 - os: linux compiler: linux-clang35-dev - addons: - apt: - sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.5' BUILD_TYPE='Dev' + env: CC='clang-3.5' CXX='clang++-3.5' BUILD_TYPE='Dev' + addons: *clang35 + + - os: linux + compiler: linux-clang38-release + env: CC='clang-3.8' CXX='clang++-3.8' BUILD_TYPE='Release' + addons: *clang38 + - os: linux + compiler: linux-clang38-dev + env: CC='clang-3.8' CXX='clang++-3.8' BUILD_TYPE='Dev' + addons: *clang38 - os: linux - compiler: linux-clang37-release - addons: - apt: - sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.7' BUILD_TYPE='Release' + compiler: linux-clang39-release + env: CC='clang-3.9' CXX='clang++-3.9' BUILD_TYPE='Release' + addons: *clang39 - os: linux - compiler: linux-clang37-dev - addons: - apt: - sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.7' BUILD_TYPE='Dev' + compiler: linux-clang39-dev + env: CC='clang-3.9' CXX='clang++-3.9' BUILD_TYPE='Dev' + addons: *clang39 + - os: linux + compiler: linux-clang40-release + env: CC='clang-4.0' CXX='clang++-4.0' BUILD_TYPE='Release' + addons: *clang40 - os: linux - compiler: linux-clang38-release - addons: - apt: - sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.8' BUILD_TYPE='Release' + compiler: linux-clang40-dev + env: CC='clang-4.0' CXX='clang++-4.0' BUILD_TYPE='Dev' + addons: *clang40 - os: linux - compiler: linux-clang38-dev - addons: - apt: - sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.8' BUILD_TYPE='Dev' + compiler: linux-clang50-release + env: CC='clang-5.0' CXX='clang++-5.0' BUILD_TYPE='Release' + addons: *clang50 + - os: linux + compiler: linux-clang50-dev + env: CC='clang-5.0' CXX='clang++-5.0' BUILD_TYPE='Dev' + addons: *clang50 + +# Disabled because it creates false-positives on the old travis systems +# - os: linux +# compiler: linux-clang50-debug +# env: CC='clang-5.0' CXX='clang++-5.0' BUILD_TYPE='Debug' +# CXXFLAGS="-fsanitize=address,undefined,integer -fno-sanitize-recover=all -fno-omit-frame-pointer" +# LDFLAGS="-fsanitize=address,undefined,integer" +# # LSAN doesn't work on container-based system +# sudo: required +# addons: *clang50 # 2/ Linux GCC Builds - os: linux compiler: linux-gcc48-release - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['g++-4.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='g++-4.8' COMPILER_FLAGS='-Wno-return-type' BUILD_TYPE='Release' + env: CC='gcc-4.8' CXX='g++-4.8' BUILD_TYPE='Release' + CXXFLAGS='-Wno-return-type' + addons: *gcc48 - os: linux compiler: linux-gcc48-dev - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['g++-4.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='g++-4.8' COMPILER_FLAGS='-Wno-return-type' BUILD_TYPE='Dev' - + env: CC='gcc-4.8' CXX='g++-4.8' BUILD_TYPE='Dev' + CXXFLAGS='-Wno-return-type' + addons: *gcc48 - os: linux compiler: linux-gcc49-release - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['g++-4.9', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='g++-4.9' BUILD_TYPE='Release' + env: CC='gcc-4.9' CXX='g++-4.9' BUILD_TYPE='Release' + addons: *gcc49 - os: linux compiler: linux-gcc49-dev - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['g++-4.9', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='g++-4.9' BUILD_TYPE='Dev' - + env: CC='gcc-4.9' CXX='g++-4.9' BUILD_TYPE='Dev' + addons: *gcc49 - os: linux compiler: linux-gcc5-release - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['g++-5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='g++-5' BUILD_TYPE='Release' + env: CC='gcc-5' CXX='g++-5' BUILD_TYPE='Release' + addons: *gcc5 - os: linux compiler: linux-gcc5-dev - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['g++-5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='g++-5' BUILD_TYPE='Dev' - + env: CC='gcc-5' CXX='g++-5' BUILD_TYPE='Dev' + addons: *gcc5 - os: linux compiler: linux-gcc6-release - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['g++-6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='g++-6' BUILD_TYPE='Release' + env: CC='gcc-6' CXX='g++-6' BUILD_TYPE='Release' + addons: *gcc6 - os: linux compiler: linux-gcc6-dev - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['g++-6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='g++-6' BUILD_TYPE='Dev' + env: CC='gcc-6' CXX='g++-6' BUILD_TYPE='Dev' + addons: *gcc6 - os: linux compiler: linux-gcc6-coverage - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['g++-6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='g++-6' BUILD_TYPE='Coverage' - + env: CC='gcc-6' CXX='g++-6' BUILD_TYPE='Coverage' + addons: *gcc6 # 3/ OSX Clang Builds - os: osx osx_image: xcode6.4 compiler: xcode64-clang-release - env: COMPILER='clang++' BUILD_TYPE='Release' + env: CC='clang' CXX='clang++' BUILD_TYPE='Release' - os: osx osx_image: xcode6.4 compiler: xcode64-clang-dev - env: COMPILER='clang++' BUILD_TYPE='Dev' - + env: CC='clang' CXX='clang++' BUILD_TYPE='Dev' - os: osx osx_image: xcode7 compiler: xcode7-clang-release - env: COMPILER='clang++' BUILD_TYPE='Release' + env: CC='clang' CXX='clang++' BUILD_TYPE='Release' - os: osx osx_image: xcode7 compiler: xcode7-clang-dev - env: COMPILER='clang++' BUILD_TYPE='Dev' - + env: CC='clang' CXX='clang++' BUILD_TYPE='Dev' - os: osx osx_image: xcode8.3 compiler: xcode8-clang-release - env: COMPILER='clang++' BUILD_TYPE='Release' + env: CC='clang' CXX='clang++' BUILD_TYPE='Release' - os: osx osx_image: xcode8.3 compiler: xcode8-clang-dev - env: COMPILER='clang++' BUILD_TYPE='Dev' + env: CC='clang' CXX='clang++' BUILD_TYPE='Dev' install: - - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" - - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} - - git clone --quiet --depth 1 https://github.com/osmcode/osm-testdata.git + - git clone --quiet --depth 1 https://github.com/mapbox/protozero.git ../protozero - | if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then brew remove gdal @@ -197,8 +220,9 @@ install: before_script: - cd ${TRAVIS_BUILD_DIR} + - git submodule update --init - mkdir build && cd build - - CXX=${COMPILER} CXXFLAGS=${COMPILER_FLAGS} cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_WITH_CCACHE=1 -DOSM_TESTDATA="${TRAVIS_BUILD_DIR}/deps/osm-testdata" + - cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_DATA_TESTS=ON -DBUILD_WITH_CCACHE=1 script: - make VERBOSE=1 && ctest --output-on-failure @@ -208,11 +232,12 @@ after_success: if [ "${BUILD_TYPE}" = "Coverage" ]; then curl -S -f https://codecov.io/bash -o codecov chmod +x codecov - gcov-${COMPILER#g++-} -p $(find test/CMakeFiles -name '*.o') - ./codecov -Z -c -F unit_tests - gcov-${COMPILER#g++-} -p $(find test/data-tests -name '*.o') - ./codecov -Z -c -F data_tests - gcov-${COMPILER#g++-} -p $(find examples -name '*.o') - ./codecov -Z -c -F examples + gcov-${CXX#g++-} -p $(find test/CMakeFiles -name '*.o') + ./codecov -Z -c -X gcov -F unit_tests + gcov-${CXX#g++-} -p $(find test/data-tests -name '*.o') + ./codecov -Z -c -X gcov -F data_tests + gcov-${CXX#g++-} -p $(find examples -name '*.o') + ./codecov -Z -c -X gcov -F examples fi +#----------------------------------------------------------------------------- diff --git a/third_party/libosmium/CHANGELOG.md b/third_party/libosmium/CHANGELOG.md index 2493e0af8ae..fa838f50b5f 100644 --- a/third_party/libosmium/CHANGELOG.md +++ b/third_party/libosmium/CHANGELOG.md @@ -2,7 +2,7 @@ # Change Log All notable changes to this project will be documented in this file. -This project adheres to [Semantic Versioning](http://semver.org/). +This project adheres to [Semantic Versioning](https://semver.org/). ## [unreleased] - @@ -13,6 +13,90 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +## [2.14.0] - 2018-03-31 + +### Added + +* Add `ReaderWithProgressBar` class. This wraps an `osmium::io::Reader` and an + `osmium::ProgressBar` into a nice little package allowing easier use in the + common case. +* Add polygon implementation for WKT and GeoJSON geometry factories. (Thanks + to Horace Williams.) +* Various tests. + +### Changed + +* Add git submodule with `osm-testdata` repository. Before this the repository + had to be installed externally. Now a submodule update can be used to get the + correct version of the osm-testdata repository. +* The XML file reader was rewritten to be more strict. Cases where it could + be tricked into failing badly were removed. There are now more tests for the + XML parser. +* Replaced `strftime` by our own implementation. Uses a specialized + implementation for our use case instead the more general `strftime`. + Benchmarked this to be faster. +* Changed the way IDs are parsed from strings. No asserts are used any more but + checks are done and an exception is thrown when IDs are out of range. This + also changes the way negative values are handled. The value `-1` is now + always accepted for all IDs and returned as `0`. This deprecates the + `string_to_user_id()` function, use `string_to_uid()` instead which returns a + different type. +* It was always a bit confusing that some of the util classes and functions are + directly in the `osmium` namespace and some are in `osmium::util`. The + `osmium::util` namespace is now declared `inline`. which allows all util + classes and functions to be addressed directly in the `osmium` namespace + while keeping backwards compatibility. +* An error is now thrown when the deprecated `pbf_add_metadata` file format + option is used. Use `add_metadata` instead. +* Extended the `add_metadata` file format option. In addition to allowing the + values `true`, `yes`, `false`, and `no`, the new values `all` and `none` + are now recognized. The option can also be set to a list of attributes + separated by the `+` sign. Attributes are `version`, `timestamp`, + `changeset`, `uid`, and `user`. All output formats have been updated to + only output the specified attributes. This is based on the new + `osmium::metadata_options` class which stores information about what metadata + an `OSMObject` has or should have. (Thanks to Michael Reichert.) +* The `<` (less than) operator on `OSMObject`s now ignores the case when + one or both of the timestamps on the objects are not set at all. This + allows better handling of OSM data files with reduced metadata. +* Allow `version = -1` and `changeset = -1` in PBF input. This value is + sometimes used by other programs to denote "no value". Osmium uses the `0` + for this. +* The example programs using the `getopt_long` function have been rewritten to + work without it. This makes using libosmium on Windows easier, where this + function is not available. +* Removed the embedded protozero from repository. Like other dependencies you + have to install protozero first. If you check out the protozero repository + in the same directory where you checked out libosmium, libosmium's CMake + will find it. +* Various code cleanups, fixing of include order, etc. +* Remove need for `winsock2` library in Windows by using code from Protozero. + (Thanks alex85k.) +* Add MSYS2 build to Appveyor and fixed some Windows compile issues. (Thanks + to alex85k.) +* Use array instead of map to store input/output format creators. +* Update included `catch.hpp` to version 1.12.1. + +### Fixed + +* Remove check for lost ways in multipolygon assembler. This rules out too many + valid multipolygons, more specifically more complex ones with touching inner + rings. +* Use different macro magic for registering index maps. This allows the maps + to be used for several types at the same time. +* Lots of code was rewritten to fix warnings reported by `clang-tidy` making + libosmium more robust. +* Make ADL work for `begin()`/`end()` of `InputIterator`. +* Various fixes to make the code more robust, including an undefined behaviour + in the debug output format and a buffer overflow in the o5m parser. +* Range checks in o5m parser throw exceptions now instead of triggering + assertions. +* Better checking that PBF data is in range. +* Check `read` and `write` system calls for `EINTR`. +* Use tag and type from protozero to make PBF parser more robust. +* Test `testdata-multipolygon` on Windows was using the wrong executable name. + + ## [2.13.1] - 2017-08-25 ### Added @@ -739,7 +823,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). Doxygen (up to version 1.8.8). This version contains a workaround to fix this. -[unreleased]: https://github.com/osmcode/libosmium/compare/v2.13.1...HEAD +[unreleased]: https://github.com/osmcode/libosmium/compare/v2.14.0...HEAD +[2.14.0]: https://github.com/osmcode/libosmium/compare/v2.13.1...v2.14.0 [2.13.1]: https://github.com/osmcode/libosmium/compare/v2.13.0...v2.13.1 [2.13.0]: https://github.com/osmcode/libosmium/compare/v2.12.2...v2.13.0 [2.12.2]: https://github.com/osmcode/libosmium/compare/v2.12.1...v2.12.2 diff --git a/third_party/libosmium/CMakeLists.txt b/third_party/libosmium/CMakeLists.txt index e27887590ae..a8409133f68 100644 --- a/third_party/libosmium/CMakeLists.txt +++ b/third_party/libosmium/CMakeLists.txt @@ -24,8 +24,8 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev;Cover project(libosmium) set(LIBOSMIUM_VERSION_MAJOR 2) -set(LIBOSMIUM_VERSION_MINOR 13) -set(LIBOSMIUM_VERSION_PATCH 1) +set(LIBOSMIUM_VERSION_MINOR 14) +set(LIBOSMIUM_VERSION_PATCH 0) set(LIBOSMIUM_VERSION "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}") @@ -62,7 +62,6 @@ option(BUILD_BENCHMARKS "compile benchmark programs" ${dev_build}) option(BUILD_DATA_TESTS "compile data tests, please run them with ctest" ${data_test_build}) option(INSTALL_GDALCPP "also install gdalcpp headers" OFF) -option(INSTALL_PROTOZERO "also install protozero headers" OFF) option(INSTALL_UTFCPP "also install utfcpp headers" OFF) option(WITH_PROFILING "add flags needed for profiling" OFF) @@ -173,17 +172,6 @@ set(_own_index) include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS}) -if(MSVC) - find_path(GETOPT_INCLUDE_DIR getopt.h) - find_library(GETOPT_LIBRARY NAMES wingetopt) - if(GETOPT_INCLUDE_DIR AND GETOPT_LIBRARY) - include_directories(SYSTEM ${GETOPT_INCLUDE_DIR}) - list(APPEND OSMIUM_LIBRARIES ${GETOPT_LIBRARY}) - else() - set(GETOPT_MISSING 1) - endif() -endif() - #----------------------------------------------------------------------------- # @@ -402,21 +390,22 @@ if(BUILD_HEADERS) file(MAKE_DIRECTORY header_check) foreach(hpp ${ALL_HPPS}) - string(REPLACE ".hpp" "" tmp ${hpp}) - string(REPLACE "/" "__" libname ${tmp}) + if(GDAL_FOUND OR NOT ((hpp STREQUAL "osmium/area/problem_reporter_ogr.hpp") OR (hpp STREQUAL "osmium/geom/ogr.hpp"))) + string(REPLACE ".hpp" "" tmp ${hpp}) + string(REPLACE "/" "__" libname ${tmp}) - # Create a dummy .cpp file that includes the header file we want to - # check. - set(DUMMYCPP ${CMAKE_BINARY_DIR}/header_check/${libname}.cpp) - file(WRITE ${DUMMYCPP} "#include <${hpp}>\n") + # Create a dummy .cpp file that includes the header file we want to + # check. + set(DUMMYCPP ${CMAKE_BINARY_DIR}/header_check/${libname}.cpp) + file(WRITE ${DUMMYCPP} "#include <${hpp}> // IWYU pragma: keep\n") - # There is no way in CMake to just compile but not link a C++ file, - # so we pretend to build a library here. - add_library(${libname} STATIC ${DUMMYCPP} include/${hpp}) - - #### this is better but only supported from cmake 3.0: - ###add_library(${libname} OBJECT ${DUMMYCPP} include/${hpp}) + # There is no way in CMake to just compile but not link a C++ file, + # so we pretend to build a library here. + add_library(${libname} STATIC ${DUMMYCPP} include/${hpp}) + #### this is better but only supported from cmake 3.0: + ###add_library(${libname} OBJECT ${DUMMYCPP} include/${hpp}) + endif() endforeach() endif() @@ -427,10 +416,10 @@ endif() # #----------------------------------------------------------------------------- message(STATUS "Looking for clang-tidy") -find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-3.9 clang-tidy-3.8 clang-tidy-3.7 clang-tidy-3.6 clang-tidy-3.5) +find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-6.0 clang-tidy-5.0) if(CLANG_TIDY) - message(STATUS "Looking for clang-tidy - found") + message(STATUS "Looking for clang-tidy - found ${CLANG_TIDY}") if(BUILD_EXAMPLES) file(GLOB CT_ALL_EXAMPLES examples/*.cpp) @@ -467,41 +456,9 @@ if(CLANG_TIDY) ${CT_ALL_BENCHMARKS} ${CT_ALL_DATA_TESTS}) - # For a list of check options, see: - # http://clang.llvm.org/extra/clang-tidy/checks/list.html - - list(APPEND CT_CHECKS "cert-*" - "-cert-err60-cpp") # even the std lib doesn't do this - - # disabled, because it is slow -# list(APPEND CT_CHECKS "clang-analyzer-*") - - list(APPEND CT_CHECKS "google-*" - "-google-explicit-constructor" - "-google-readability-casting" - "-google-readability-function") - - list(APPEND CT_CHECKS "llvm-*" - "-llvm-include-order") - - list(APPEND CT_CHECKS "misc-*" - "-misc-argument-comment") - - list(APPEND CT_CHECKS "modernize-*" - "-modernize-make-unique") # not available in C++11 - - list(APPEND CT_CHECKS "readability-*" - "-readability-identifier-naming" - "-readability-implicit-bool-cast" - "-readability-named-parameter") - - string(REPLACE ";" "," ALL_CHECKS "${CT_CHECKS}") - add_custom_target(clang-tidy ${CLANG_TIDY} -p ${CMAKE_BINARY_DIR} - -header-filter='include/osmium/.*' - -checks="${ALL_CHECKS}" ${CT_CHECK_FILES} ) else() @@ -523,10 +480,6 @@ if(INSTALL_GDALCPP) install(FILES include/gdalcpp.hpp DESTINATION include) endif() -if(INSTALL_PROTOZERO) - install(DIRECTORY include/protozero DESTINATION include) -endif() - if(INSTALL_UTFCPP) install(FILES include/utf8.h DESTINATION include) install(DIRECTORY include/utf8 DESTINATION include) @@ -560,8 +513,7 @@ include(CPack) if(BUILD_DATA_TESTS AND OSM_TESTDATA STREQUAL "OSM_TESTDATA-NOTFOUND") message("\n========================== WARNING ==========================") message("osm-testdata directory not found, data tests were disabled!\n") - message("You can get it from https://github.com/osmcode/osm-testdata") - message("Clone it into the same directory libosmium is in") + message("Call 'git submodule update --init' to install test data") message("or set the OSM_TESTDATA cmake variable to its path.") message("=============================================================\n") endif() diff --git a/third_party/libosmium/CONTRIBUTING.md b/third_party/libosmium/CONTRIBUTING.md index 7e51d4cd47e..a60f507db0c 100644 --- a/third_party/libosmium/CONTRIBUTING.md +++ b/third_party/libosmium/CONTRIBUTING.md @@ -10,3 +10,10 @@ Some rules for contributing to this project: you have added. Please read the [notes for developers](NOTES_FOR_DEVELOPERS.md) beforehand which contains some coding guidelines. +If you are reporting a problem: + +* Describe exactly what you were trying to achieve, what you did, what you + expected to happen and what did happen instead. Include relevant information + about the platform, OS version etc. you are using. Include shell commands you + typed in, log files, errors messages etc. + diff --git a/third_party/libosmium/EXTERNAL_LICENSES.txt b/third_party/libosmium/EXTERNAL_LICENSES.txt index 7b06fcf4110..9484c08913f 100644 --- a/third_party/libosmium/EXTERNAL_LICENSES.txt +++ b/third_party/libosmium/EXTERNAL_LICENSES.txt @@ -1,210 +1,4 @@ -==== For protozero from https://github.com/mapbox/protozero - -protozero copyright (c) Mapbox. - -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. - -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. - -==== For protozero from https://github.com/mapbox/protozero - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - ==== For utf8.h Copyright 2006 Nemanja Trifunovic diff --git a/third_party/libosmium/LICENSE.txt b/third_party/libosmium/LICENSE similarity index 100% rename from third_party/libosmium/LICENSE.txt rename to third_party/libosmium/LICENSE diff --git a/third_party/libosmium/NOTES_FOR_DEVELOPERS.md b/third_party/libosmium/NOTES_FOR_DEVELOPERS.md index 4bd9d68cf7a..7ffca5ec580 100644 --- a/third_party/libosmium/NOTES_FOR_DEVELOPERS.md +++ b/third_party/libosmium/NOTES_FOR_DEVELOPERS.md @@ -126,7 +126,7 @@ Call `cmake/iwyu.sh` to check for proper includes and forward declarations. This uses the clang-based `include-what-you-use` program. Note that it does produce some false reports and crashes often. The `osmium.imp` file can be used to define mappings for iwyu. See the IWYU tool at -. +. ## Testing diff --git a/third_party/libosmium/README.md b/third_party/libosmium/README.md index b4fc4dcdb32..91d8f507c35 100644 --- a/third_party/libosmium/README.md +++ b/third_party/libosmium/README.md @@ -9,6 +9,7 @@ Libosmium works on Linux, Mac OSX and Windows. [![Travis Build Status](https://secure.travis-ci.org/osmcode/libosmium.svg)](https://travis-ci.org/osmcode/libosmium) [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/osmcode/libosmium?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium) [![Coverage Status](https://codecov.io/gh/osmcode/libosmium/branch/master/graph/badge.svg)](https://codecov.io/gh/osmcode/libosmium) +[![Packaging status](https://repology.org/badge/tiny-repos/libosmium.svg)](https://repology.org/metapackage/libosmium) Please see the [Libosmium manual](http://osmcode.org/libosmium/manual.html) for more details than this README can provide. @@ -31,9 +32,11 @@ manual. The following external (header-only) libraries are included in the libosmium repository: * [gdalcpp](https://github.com/joto/gdalcpp) -* [protozero](https://github.com/mapbox/protozero) * [utfcpp](http://utfcpp.sourceforge.net/) +Note that [protozero](https://github.com/mapbox/protozero) was included in +earlier versions of libosmium, but isn't any more. + ## Directories @@ -73,6 +76,14 @@ chapter in the manual. ## Testing +To download the `osm-testdata` submodule call: + +``` +git submodule update --init +``` + +This will enable additional tests. + See the [Libosmium Manual](http://osmcode.org/libosmium/manual.html#running-tests) for instructions. diff --git a/third_party/libosmium/appveyor.yml b/third_party/libosmium/appveyor.yml index ca77ae64750..2ac3e254665 100644 --- a/third_party/libosmium/appveyor.yml +++ b/third_party/libosmium/appveyor.yml @@ -6,34 +6,54 @@ environment: matrix: - - config: Dev + - config: MSYS2 autocrlf: true - - config: Dev - autocrlf: false - - config: RelWithDebInfo + - config: Debug + autocrlf: true + - config: Release autocrlf: true - - config: RelWithDebInfo + - config: Debug + autocrlf: false + - config: Release autocrlf: false -shallow_clone: true +clone_depth: 1 # Operating system (build VM template) os: Visual Studio 2015 +platform: x64 + # scripts that are called at very beginning, before repo cloning init: - git config --global core.autocrlf %autocrlf% - git config --get core.autocrlf -# clone directory clone_folder: c:\projects\libosmium -platform: x64 +# The option --ask=20 is a workaround for a problem with the MSYS2 update +# process. Without it the following error is printed and the appveyor script +# halts: "msys2-runtime and catgets are in conflict. Remove catgets?" +# See also: https://github.com/Alexpux/MSYS2-packages/issues/1141 +install: + - git submodule update --init + - cd c:\projects + - git clone --depth 1 https://github.com/mapbox/protozero + - if [%config%]==[MSYS2] ( + C:\msys64\usr\bin\pacman --noconfirm --sync --refresh --refresh --sysupgrade --sysupgrade --ask=20 + && C:\msys64\usr\bin\pacman -Rc --noconfirm mingw-w64-x86_64-gcc-libs + ) build_script: - - build-appveyor.bat + - cd c:\projects\libosmium + - if [%config%]==[MSYS2] ( + build-msys2.bat + ) else ( + build-appveyor.bat + ) # remove garbage VS messages -# http://help.appveyor.com/discussions/problems/4569-the-target-_convertpdbfiles-listed-in-a-beforetargets-attribute-at-c-does-not-exist-in-the-project-and-will-be-ignored +# https://help.appveyor.com/discussions/problems/4569-the-target-_convertpdbfiles-listed-in-a-beforetargets-attribute-at-c-does-not-exist-in-the-project-and-will-be-ignored before_build: - del "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets" + diff --git a/third_party/libosmium/benchmarks/download_data.sh b/third_party/libosmium/benchmarks/download_data.sh index be6adb9541d..b17fcad5895 100755 --- a/third_party/libosmium/benchmarks/download_data.sh +++ b/third_party/libosmium/benchmarks/download_data.sh @@ -4,9 +4,9 @@ # cd $DATA_DIR -curl --location --output 1_liechtenstein.osm.pbf http://download.geofabrik.de/europe/liechtenstein-latest.osm.pbf # about 2 MB -curl --location --output 2_bremen.osm.pbf http://download.geofabrik.de/europe/germany/bremen-latest.osm.pbf # about 16 MB -curl --location --output 3_sachsen.osm.pbf http://download.geofabrik.de/europe/germany/sachsen-latest.osm.pbf # about 160 MB -curl --location --output 4_germany.osm.pbf http://download.geofabrik.de/europe/germany-latest.osm.pbf # about 3 GB -curl --location --output 5_planet.osm.pbf http://planet.osm.org/pbf/planet-latest.osm.pbf # about 35 GB +curl --location --output 1_liechtenstein.osm.pbf https://download.geofabrik.de/europe/liechtenstein-latest.osm.pbf # about 2 MB +curl --location --output 2_bremen.osm.pbf https://download.geofabrik.de/europe/germany/bremen-latest.osm.pbf # about 16 MB +curl --location --output 3_sachsen.osm.pbf https://download.geofabrik.de/europe/germany/sachsen-latest.osm.pbf # about 160 MB +curl --location --output 4_germany.osm.pbf https://download.geofabrik.de/europe/germany-latest.osm.pbf # about 3 GB +curl --location --output 5_planet.osm.pbf https://planet.osm.org/pbf/planet-latest.osm.pbf # about 35 GB diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_count.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_count.cpp index 41f9aa07890..65051b0f94f 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_count.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_count.cpp @@ -4,36 +4,35 @@ */ +#include +#include +#include + #include #include #include #include -#include -#include -#include - struct CountHandler : public osmium::handler::Handler { uint64_t nodes = 0; uint64_t ways = 0; uint64_t relations = 0; - void node(const osmium::Node&) { + void node(const osmium::Node& /*node*/) { ++nodes; } - void way(const osmium::Way&) { + void way(const osmium::Way& /*way*/) { ++ways; } - void relation(const osmium::Relation&) { + void relation(const osmium::Relation& /*relation*/) { ++relations; } }; - int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; @@ -48,8 +47,8 @@ int main(int argc, char* argv[]) { osmium::apply(reader, handler); reader.close(); - std::cout << "Nodes: " << handler.nodes << "\n"; - std::cout << "Ways: " << handler.ways << "\n"; - std::cout << "Relations: " << handler.relations << "\n"; + std::cout << "Nodes: " << handler.nodes << '\n'; + std::cout << "Ways: " << handler.ways << '\n'; + std::cout << "Relations: " << handler.relations << '\n'; } diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_count_tag.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_count_tag.cpp index 5b82a7c3abd..12d5c8201ce 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_count_tag.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_count_tag.cpp @@ -4,15 +4,15 @@ */ +#include +#include +#include + #include #include #include #include -#include -#include -#include - struct CountHandler : public osmium::handler::Handler { uint64_t counter = 0; @@ -26,17 +26,16 @@ struct CountHandler : public osmium::handler::Handler { } } - void way(const osmium::Way&) { + void way(const osmium::Way& /*way*/) { ++all; } - void relation(const osmium::Relation&) { + void relation(const osmium::Relation& /*relation*/) { ++all; } }; - int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; @@ -51,6 +50,6 @@ int main(int argc, char* argv[]) { osmium::apply(reader, handler); reader.close(); - std::cout << "r_all=" << handler.all << " r_counter=" << handler.counter << "\n"; + std::cout << "r_all=" << handler.all << " r_counter=" << handler.counter << '\n'; } diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_index_map.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_index_map.cpp index 2cf550ca9c5..e8989824685 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_index_map.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_index_map.cpp @@ -4,16 +4,15 @@ */ -#include -#include -#include - -#include +#include #include +#include +#include #include -#include -#include +#include +#include +#include using index_type = osmium::index::map::Map; diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_mercator.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_mercator.cpp index 091e5c025ef..f9672c305ce 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_mercator.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_mercator.cpp @@ -4,17 +4,17 @@ */ +#include +#include +#include +#include +#include + #include #include #include #include -#include -#include -#include -#include -#include - struct GeomHandler : public osmium::handler::Handler { osmium::geom::WKBFactory factory; @@ -25,7 +25,6 @@ struct GeomHandler : public osmium::handler::Handler { }; - int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp index e9f950c3168..1a689b18785 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp @@ -16,6 +16,12 @@ */ +#include +#include +#include +#include +#include + #include #include #include @@ -24,13 +30,6 @@ #include #include -#include -#include -#include - -#include -#include - using static_index_type = osmium::index::map::SparseMemArray; const std::string location_store{"sparse_mem_array"}; @@ -51,7 +50,7 @@ int main(int argc, char* argv[]) { const auto& map_factory = osmium::index::MapFactory::instance(); - const auto buffer_size = buffer.committed() / (1024*1024); // buffer size in MBytes + const auto buffer_size = buffer.committed() / (1024 * 1024); // buffer size in MBytes const int runs = std::max(10, static_cast(5000ull / buffer_size)); std::cout << "input: filename=" << input_filename << " buffer_size=" << buffer_size << "MBytes\n"; @@ -82,10 +81,14 @@ int main(int argc, char* argv[]) { osmium::apply(tmp_buffer, static_location_handler); const auto end = std::chrono::steady_clock::now(); - const double duration = std::chrono::duration(end-start).count(); + const double duration = std::chrono::duration(end - start).count(); - if (duration < static_min) static_min = duration; - if (duration > static_max) static_max = duration; + if (duration < static_min) { + static_min = duration; + } + if (duration > static_max) { + static_max = duration; + } static_sum += duration; } @@ -105,16 +108,20 @@ int main(int argc, char* argv[]) { osmium::apply(tmp_buffer, dynamic_location_handler); const auto end = std::chrono::steady_clock::now(); - const double duration = std::chrono::duration(end-start).count(); + const double duration = std::chrono::duration(end - start).count(); - if (duration < dynamic_min) dynamic_min = duration; - if (duration > dynamic_max) dynamic_max = duration; + if (duration < dynamic_min) { + dynamic_min = duration; + } + if (duration > dynamic_max) { + dynamic_max = duration; + } dynamic_sum += duration; } } - const double static_avg = static_sum/runs; - const double dynamic_avg = dynamic_sum/runs; + const double static_avg = static_sum / runs; + const double dynamic_avg = dynamic_sum / runs; std::cout << "static min=" << static_min << "ms avg=" << static_avg << "ms max=" << static_max << "ms\n"; std::cout << "dynamic min=" << dynamic_min << "ms avg=" << dynamic_avg << "ms max=" << dynamic_max << "ms\n"; diff --git a/third_party/libosmium/build-appveyor.bat b/third_party/libosmium/build-appveyor.bat index ef9da5ffd1e..74ea0c91a94 100644 --- a/third_party/libosmium/build-appveyor.bat +++ b/third_party/libosmium/build-appveyor.bat @@ -4,73 +4,33 @@ SET EL=0 ECHO ~~~~~~ %~f0 ~~~~~~ -SET CUSTOM_CMAKE=cmake-3.6.2-win64-x64 -::show all available env vars SET -ECHO cmake on AppVeyor -cmake -version ECHO activating VS cmd prompt && CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 IF %ERRORLEVEL% NEQ 0 GOTO ERROR -SET lodir=%CD% -SET PATH=%lodir%\%CUSTOM_CMAKE%\bin;%PATH% -SET LODEPSDIR=%lodir%\libosmium-deps -SET PROJ_LIB=%LODEPSDIR%\proj\share -SET GDAL_DATA=%LODEPSDIR%\gdal\data -::gdal.dll -SET PATH=%LODEPSDIR%\gdal\lib;%PATH% -::geos.dll -SET PATH=%LODEPSDIR%\geos\lib;%PATH% -::libtiff.dll -SET PATH=%LODEPSDIR%\libtiff\lib;%PATH% -::jpeg.dll -SET PATH=%LODEPSDIR%\jpeg\lib;%PATH% -::libexpat.dll -SET PATH=%LODEPSDIR%\expat\lib;%PATH% -::zlibwapi.dll -SET PATH=%LODEPSDIR%\zlib\lib;%PATH% -::convert backslashes in bzip2 path to forward slashes -::cmake cannot find it otherwise -SET LIBBZIP2=%LODEPSDIR%\bzip2\lib\libbz2.lib -SET LIBBZIP2=%LIBBZIP2:\=/% - -IF NOT EXIST cm.7z ECHO downloading cmake %CUSTOM_CMAKE% ... && powershell Invoke-WebRequest https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/%CUSTOM_CMAKE%.7z -OutFile cm.7z -IF %ERRORLEVEL% NEQ 0 GOTO ERROR +CD .. -IF NOT EXIST lodeps.7z ECHO downloading binary dependencies... && powershell Invoke-WebRequest https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/libosmium-deps-win-14.0-x64.7z -OutFile lodeps.7z +nuget install expat.v140 -Version 2.2.5 IF %ERRORLEVEL% NEQ 0 GOTO ERROR -IF NOT EXIST %CUSTOM_CMAKE% ECHO extracting cmake... && 7z x cm.7z | %windir%\system32\find "ing archive" -IF %ERRORLEVEL% NEQ 0 GOTO ERROR +SET PATH=C:/projects/expat.v140.2.2.5/build/native/bin/x64/%config%;%PATH% -IF NOT EXIST %LODEPSDIR% ECHO extracting binary dependencies... && 7z x lodeps.7z | %windir%\system32\find "ing archive" +nuget install zlib-vc140-static-64 -Version 1.2.11 IF %ERRORLEVEL% NEQ 0 GOTO ERROR -ECHO %LODEPSDIR% -DIR %LODEPSDIR% -::TREE %LODEPSDIR% - -::powershell (Get-ChildItem $env:LODEPSDIR\boost\lib -Filter *boost*.dll)[0].BaseName.split('_')[-1] -FOR /F "tokens=1 usebackq" %%i in (`powershell ^(Get-ChildItem %LODEPSDIR%\boost\lib -Filter *boost*.dll^)[0].BaseName.split^('_'^)[-1]`) DO SET BOOST_VERSION=%%i +nuget install bzip2.v140 -Version 1.0.6.9 IF %ERRORLEVEL% NEQ 0 GOTO ERROR -ECHO BOOST_VERSION^: %BOOST_VERSION% +SET PATH=C:/projects/bzip2.v140.1.0.6.9/build/native/bin/x64/%config%;%PATH% -ECHO our own cmake -cmake -version +CD libosmium -CD %lodir%\.. +ECHO config^: %config% -IF NOT EXIST osm-testdata ECHO cloning osm-testdata && git clone --depth 1 https://github.com/osmcode/osm-testdata.git -IF %ERRORLEVEL% NEQ 0 GOTO ERROR -CD osm-testdata -git fetch -IF %ERRORLEVEL% NEQ 0 GOTO ERROR -git pull -IF %ERRORLEVEL% NEQ 0 GOTO ERROR +SET libpostfix="" +IF "%config%"=="Debug" SET libpostfix="d" -CD %lodir% IF EXIST build ECHO deleting build dir... && RD /Q /S build IF %ERRORLEVEL% NEQ 0 GOTO ERROR @@ -78,20 +38,21 @@ MKDIR build IF %ERRORLEVEL% NEQ 0 GOTO ERROR CD build -ECHO config^: %config% ::This will produce lots of LNK4099 warnings which can be ignored. ::Unfortunately they can't be disabled, see -::http://stackoverflow.com/questions/661606/visual-c-how-to-disable-specific-linker-warnings -SET CMAKE_CMD=cmake .. ^ --LA -G "Visual Studio 14 Win64" ^ +::https://stackoverflow.com/questions/661606/visual-c-how-to-disable-specific-linker-warnings +SET CMAKE_CMD=cmake .. -LA -G "Visual Studio 14 Win64" ^ -DOsmium_DEBUG=TRUE ^ -DCMAKE_BUILD_TYPE=%config% ^ -DBUILD_HEADERS=OFF ^ --DBOOST_ROOT=%LODEPSDIR%\boost ^ --DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib ^ --DBZIP2_LIBRARY_RELEASE=%LIBBZIP2% ^ --DCMAKE_PREFIX_PATH=%LODEPSDIR%\zlib;%LODEPSDIR%\expat;%LODEPSDIR%\bzip2;%LODEPSDIR%\geos;%LODEPSDIR%\gdal;%LODEPSDIR%\proj;%LODEPSDIR%\sparsehash;%LODEPSDIR%\wingetopt +-DBOOST_ROOT=C:/Libraries/boost_1_63_0 ^ +-DZLIB_INCLUDE_DIR=C:/projects/zlib-vc140-static-64.1.2.11/lib/native/include ^ +-DZLIB_LIBRARY=C:/projects/zlib-vc140-static-64.1.2.11/lib/native/libs/x64/static/%config%/zlibstatic.lib ^ +-DEXPAT_INCLUDE_DIR=C:/projects/expat.v140.2.2.5/build/native/include ^ +-DEXPAT_LIBRARY=C:/projects/expat.v140.2.2.5/build/native/lib/x64/%config%/libexpat%libpostfix%.lib ^ +-DBZIP2_INCLUDE_DIR=C:/projects/bzip2.v140.1.0.6.9/build/native/include ^ +-DBZIP2_LIBRARIES=C:/projects/bzip2.v140.1.0.6.9/build/native/lib/x64/%config%/libbz2%libpostfix%.lib ECHO calling^: %CMAKE_CMD% %CMAKE_CMD% @@ -107,12 +68,9 @@ msbuild libosmium.sln ^ /p:PlatformToolset=v140 %avlogger% IF %ERRORLEVEL% NEQ 0 GOTO ERROR -ctest --output-on-failure ^ --C %config% ^ --E testdata-overview +ctest --output-on-failure -C %config% -E testdata-overview IF %ERRORLEVEL% NEQ 0 GOTO ERROR - GOTO DONE :ERROR diff --git a/third_party/libosmium/build-local.bat b/third_party/libosmium/build-local.bat index 202d0b2fa68..c2d6e8d9ab3 100644 --- a/third_party/libosmium/build-local.bat +++ b/third_party/libosmium/build-local.bat @@ -6,11 +6,11 @@ ECHO ~~~~~~ %~f0 ~~~~~~ ECHO. ECHO build-local ["config=Dev"] -ECHO default config^: RelWithDebInfo +ECHO default config^: Release ECHO. SET platform=x64 -SET config=RelWithDebInfo +SET config=Release :: OVERRIDE PARAMETERS >>>>>>>> :NEXT-ARG @@ -24,9 +24,6 @@ GOTO NEXT-ARG :ARGS-DONE ::<<<<< OVERRIDE PARAMETERS -WHERE 7z -IF %ERRORLEVEL% NEQ 0 ECHO 7zip not on PATH && GOTO ERROR - CALL build-appveyor.bat IF %ERRORLEVEL% NEQ 0 GOTO ERROR diff --git a/third_party/libosmium/build-msys2.bat b/third_party/libosmium/build-msys2.bat new file mode 100644 index 00000000000..a4f0f7a1c6e --- /dev/null +++ b/third_party/libosmium/build-msys2.bat @@ -0,0 +1,41 @@ +@ECHO OFF +SETLOCAL +SET EL=0 + +ECHO ~~~~~~ %~f0 ~~~~~~ + +echo "Adding MSYS2 to path..." +SET "PATH=C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%" +echo %PATH% + +echo "Installing MSYS2 packages..." +bash -lc "pacman -S --needed --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-geos mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-cppcheck mingw-w64-x86_64-doxygen mingw-w64-x86_64-gdb mingw-w64-x86_64-sparsehash mingw-w64-x86_64-gdal mingw-w64-x86_64-ruby mingw-w64-x86_64-libspatialite mingw-w64-x86_64-spatialite-tools mingw-w64-x86_64-clang-tools-extra" +bash -lc "pacman -S --needed --noconfirm mingw-w64-x86_64-postgresql mingw-w64-x86_64-netcdf mingw-w64-x86_64-crypto++" +call C:\msys64\mingw64\bin\gem.cmd install json + +echo "Setting PROJ_LIB variable for correct PROJ.4 working" +set PROJ_LIB=c:\msys64\mingw64\share\proj + +echo "Generating makefiles" +mkdir build +cd build +cmake .. -G "MSYS Makefiles" -DBUILD_DATA_TESTS=ON -DBUILD_HEADERS=OFF +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +echo "Building" +make +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +echo "Testing" +ctest --output-on-failure +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +:ERROR +ECHO ~~~~~~ ERROR %~f0 ~~~~~~ +SET EL=%ERRORLEVEL% + +:DONE +IF %EL% NEQ 0 ECHO. && ECHO !!! ERRORLEVEL^: %EL% !!! && ECHO. +ECHO ~~~~~~ DONE %~f0 ~~~~~~ + +EXIT /b %EL% diff --git a/third_party/libosmium/cmake/FindGem.cmake b/third_party/libosmium/cmake/FindGem.cmake index f5389d1e832..5d78a9026f0 100644 --- a/third_party/libosmium/cmake/FindGem.cmake +++ b/third_party/libosmium/cmake/FindGem.cmake @@ -1,5 +1,7 @@ # Author thomas.roehr@dfki.de # +# Version 0.31 2017-09-15 +# - find gem executable gem.cmd on Windows # Version 0.3 2013-07-02 # - rely on `gem content` to find library and header # - introduce GEM_OS_PKG to allow search via pkgconfig @@ -28,7 +30,7 @@ # Check for how 'gem' should be called include(FindPackageHandleStandardArgs) find_program(GEM_EXECUTABLE - NAMES "gem${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}" + NAMES "gem.cmd" "gem${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}" "gem${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}" "gem-${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}" "gem-${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}" diff --git a/third_party/libosmium/cmake/FindOsmium.cmake b/third_party/libosmium/cmake/FindOsmium.cmake index adc457f9a2d..9eaf7349e8f 100644 --- a/third_party/libosmium/cmake/FindOsmium.cmake +++ b/third_party/libosmium/cmake/FindOsmium.cmake @@ -54,8 +54,7 @@ # #---------------------------------------------------------------------- -# This is the list of directories where we look for osmium and protozero -# includes. +# This is the list of directories where we look for osmium includes. set(_osmium_include_path ../libosmium ~/Library/Frameworks @@ -112,29 +111,14 @@ endif() if(Osmium_USE_PBF) find_package(ZLIB) find_package(Threads) - - message(STATUS "Looking for protozero") - find_path(PROTOZERO_INCLUDE_DIR protozero/version.hpp - PATH_SUFFIXES include - PATHS ${_osmium_include_path} - ${OSMIUM_INCLUDE_DIR} - ) - if(PROTOZERO_INCLUDE_DIR) - message(STATUS "Looking for protozero - found") - else() - message(STATUS "Looking for protozero - not found") - endif() + find_package(Protozero 1.5.1) list(APPEND OSMIUM_EXTRA_FIND_VARS ZLIB_FOUND Threads_FOUND PROTOZERO_INCLUDE_DIR) - if(ZLIB_FOUND AND Threads_FOUND AND PROTOZERO_INCLUDE_DIR) + if(ZLIB_FOUND AND Threads_FOUND AND PROTOZERO_FOUND) list(APPEND OSMIUM_PBF_LIBRARIES ${ZLIB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) - if(WIN32) - # This is needed for the ntohl() function - list(APPEND OSMIUM_PBF_LIBRARIES ws2_32) - endif() list(APPEND OSMIUM_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR} ${PROTOZERO_INCLUDE_DIR} @@ -361,10 +345,10 @@ endif() set(OSMIUM_DRACONIC_CLANG_OPTIONS "-Wdocumentation -Wunused-exception-parameter -Wmissing-declarations -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-unused-macros -Wno-exit-time-destructors -Wno-global-constructors -Wno-padded -Wno-switch-enum -Wno-missing-prototypes -Wno-weak-vtables -Wno-cast-align -Wno-float-equal") if(Osmium_DEBUG) - message(STATUS "OSMIUM_XML_LIBRARIES=" ${OSMIUM_XML_LIBRARIES}) - message(STATUS "OSMIUM_PBF_LIBRARIES=" ${OSMIUM_PBF_LIBRARIES}) - message(STATUS "OSMIUM_IO_LIBRARIES=" ${OSMIUM_IO_LIBRARIES}) - message(STATUS "OSMIUM_LIBRARIES=" ${OSMIUM_LIBRARIES}) - message(STATUS "OSMIUM_INCLUDE_DIRS=" ${OSMIUM_INCLUDE_DIRS}) + message(STATUS "OSMIUM_XML_LIBRARIES=${OSMIUM_XML_LIBRARIES}") + message(STATUS "OSMIUM_PBF_LIBRARIES=${OSMIUM_PBF_LIBRARIES}") + message(STATUS "OSMIUM_IO_LIBRARIES=${OSMIUM_IO_LIBRARIES}") + message(STATUS "OSMIUM_LIBRARIES=${OSMIUM_LIBRARIES}") + message(STATUS "OSMIUM_INCLUDE_DIRS=${OSMIUM_INCLUDE_DIRS}") endif() diff --git a/third_party/libosmium/cmake/FindProtozero.cmake b/third_party/libosmium/cmake/FindProtozero.cmake new file mode 100644 index 00000000000..ad16cabeb00 --- /dev/null +++ b/third_party/libosmium/cmake/FindProtozero.cmake @@ -0,0 +1,63 @@ +#---------------------------------------------------------------------- +# +# FindProtozero.cmake +# +# Find the protozero headers. +# +#---------------------------------------------------------------------- +# +# Usage: +# +# Copy this file somewhere into your project directory, where cmake can +# find it. Usually this will be a directory called "cmake" which you can +# add to the CMake module search path with the following line in your +# CMakeLists.txt: +# +# list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +# +# Then add the following in your CMakeLists.txt: +# +# find_package(Protozero [version] [REQUIRED]) +# include_directories(SYSTEM ${PROTOZERO_INCLUDE_DIR}) +# +# The version number is optional. If it is not set, any version of +# protozero will do. +# +# if(NOT PROTOZERO_FOUND) +# message(WARNING "Protozero not found!\n") +# endif() +# +#---------------------------------------------------------------------- +# +# Variables: +# +# PROTOZERO_FOUND - True if Protozero was found. +# PROTOZERO_INCLUDE_DIR - Where to find include files. +# +#---------------------------------------------------------------------- + +# find include path +find_path(PROTOZERO_INCLUDE_DIR protozero/version.hpp + PATH_SUFFIXES include + PATHS ${CMAKE_SOURCE_DIR}/../protozero +) + +# Check version number +if(Protozero_FIND_VERSION) + file(STRINGS "${PROTOZERO_INCLUDE_DIR}/protozero/version.hpp" _version_define REGEX "#define PROTOZERO_VERSION_STRING") + if("${_version_define}" MATCHES "#define PROTOZERO_VERSION_STRING \"([0-9.]+)\"") + set(_version "${CMAKE_MATCH_1}") + else() + set(_version "unknown") + endif() +endif() + +#set(PROTOZERO_INCLUDE_DIRS "${PROTOZERO_INCLUDE_DIR}") + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Protozero + REQUIRED_VARS PROTOZERO_INCLUDE_DIR + VERSION_VAR _version) + + +#---------------------------------------------------------------------- diff --git a/third_party/libosmium/examples/CMakeLists.txt b/third_party/libosmium/examples/CMakeLists.txt index c4f875f43d2..c1498cc3522 100644 --- a/third_party/libosmium/examples/CMakeLists.txt +++ b/third_party/libosmium/examples/CMakeLists.txt @@ -30,25 +30,6 @@ set(EXAMPLES ) -#----------------------------------------------------------------------------- -# -# Examples depending on wingetopt -# -#----------------------------------------------------------------------------- -set(GETOPT_EXAMPLES area_test convert index_lookup) -if(NOT GETOPT_MISSING) - foreach(example ${GETOPT_EXAMPLES}) - list(APPEND EXAMPLE_LIBS_${example} ${GETOPT_LIBRARY}) - endforeach() -else() - message(STATUS "Configuring examples - Skipping examples because on Visual Studio the wingetopt library is needed and was not found:") - foreach(example ${GETOPT_EXAMPLES}) - message(STATUS " - osmium_${example}") - list(REMOVE_ITEM EXAMPLES ${example}) - endforeach() -endif() - - #----------------------------------------------------------------------------- # # Configure examples diff --git a/third_party/libosmium/examples/osmium_area_test.cpp b/third_party/libosmium/examples/osmium_area_test.cpp index 948c032ed19..5a29472e5a4 100644 --- a/third_party/libosmium/examples/osmium_area_test.cpp +++ b/third_party/libosmium/examples/osmium_area_test.cpp @@ -26,7 +26,7 @@ */ #include // for std::exit -#include // for getopt_long +#include // for std::strcmp #include // for std::cout, std::cerr // For assembling multipolygons @@ -91,13 +91,21 @@ void print_help() { << " -o, --dump-objects Dump area objects\n"; } +void print_usage(const char* prgname) { + std::cerr << "Usage: " << prgname << " [OPTIONS] OSMFILE\n"; + std::exit(1); +} + int main(int argc, char* argv[]) { - static struct option long_options[] = { - {"help", no_argument, nullptr, 'h'}, - {"dump-wkt", no_argument, nullptr, 'w'}, - {"dump-objects", no_argument, nullptr, 'o'}, - {nullptr, 0, nullptr, 0} - }; + if (argc > 1 && (!std::strcmp(argv[1], "-h") || + !std::strcmp(argv[1], "--help"))) { + print_help(); + std::exit(0); + } + + if (argc != 3) { + print_usage(argv[0]); + } // Initialize an empty DynamicHandler. Later it will be associated // with one of the handlers. You can think of the DynamicHandler as @@ -105,35 +113,15 @@ int main(int argc, char* argv[]) { // real handler. osmium::handler::DynamicHandler handler; - // Read options from command line. - while (true) { - const int c = getopt_long(argc, argv, "hwo", long_options, nullptr); - if (c == -1) { - break; - } - - switch (c) { - case 'h': - print_help(); - std::exit(0); - case 'w': - handler.set(); - break; - case 'o': - handler.set(std::cout); - break; - default: - std::exit(1); - } - } - - const int remaining_args = argc - optind; - if (remaining_args != 1) { - std::cerr << "Usage: " << argv[0] << " [OPTIONS] OSMFILE\n"; - std::exit(1); + if (!std::strcmp(argv[1], "-w") || !std::strcmp(argv[1], "--dump-wkt")) { + handler.set(); + } else if (!std::strcmp(argv[1], "-o") || !std::strcmp(argv[1], "--dump-objects")) { + handler.set(std::cout); + } else { + print_usage(argv[0]); } - osmium::io::File input_file{argv[optind]}; + osmium::io::File input_file{argv[2]}; // Configuration for the multipolygon assembler. Here the default settings // are used, but you could change multiple settings. diff --git a/third_party/libosmium/examples/osmium_change_tags.cpp b/third_party/libosmium/examples/osmium_change_tags.cpp index ae2e99e9880..e00ee0339fe 100644 --- a/third_party/libosmium/examples/osmium_change_tags.cpp +++ b/third_party/libosmium/examples/osmium_change_tags.cpp @@ -76,14 +76,14 @@ class RewriteHandler : public osmium::handler::Handler { // Iterate over all tags and build new tags using the new builder // based on the old ones. for (const auto& tag : tags) { - if (std::strcmp(tag.key(), "created_by")) { - if (!std::strcmp(tag.key(), "landuse") && !std::strcmp(tag.value(), "forest")) { - // add_tag() can be called with key and value C strings - builder.add_tag("natural", "wood"); - } else { - // add_tag() can also be called with an osmium::Tag - builder.add_tag(tag); - } + if (!std::strcmp(tag.key(), "created_by")) { + // ignore + } else if (!std::strcmp(tag.key(), "landuse") && !std::strcmp(tag.value(), "forest")) { + // add_tag() can be called with key and value C strings + builder.add_tag("natural", "wood"); + } else { + // add_tag() can also be called with an osmium::Tag + builder.add_tag(tag); } } } diff --git a/third_party/libosmium/examples/osmium_convert.cpp b/third_party/libosmium/examples/osmium_convert.cpp index dfa0abd4a6e..d28663b893d 100644 --- a/third_party/libosmium/examples/osmium_convert.cpp +++ b/third_party/libosmium/examples/osmium_convert.cpp @@ -18,8 +18,8 @@ */ #include // for std::exit +#include // for std::strcmp #include // for std::exception -#include // for getopt_long #include // for std::cout, std::cerr #include // for std::string @@ -51,13 +51,21 @@ void print_help() { << " -t, --to-format=FORMAT Output format\n"; } +void print_usage(const char* prgname) { + std::cerr << "Usage: " << prgname << " [OPTIONS] [INFILE [OUTFILE]]\n"; + std::exit(0); +} + int main(int argc, char* argv[]) { - static struct option long_options[] = { - {"help", no_argument, nullptr, 'h'}, - {"from-format", required_argument, nullptr, 'f'}, - {"to-format", required_argument, nullptr, 't'}, - {nullptr, 0, nullptr, 0} - }; + if (argc == 1) { + print_usage(argv[0]); + } + + if (argc > 1 && (!std::strcmp(argv[1], "-h") || + !std::strcmp(argv[1], "--help"))) { + print_help(); + std::exit(0); + } // Input and output format are empty by default. Later this will mean that // the format should be taken from the input and output file suffix, @@ -65,44 +73,35 @@ int main(int argc, char* argv[]) { std::string input_format; std::string output_format; - // Read options from command line. - while (true) { - const int c = getopt_long(argc, argv, "dhf:t:", long_options, nullptr); - if (c == -1) { - break; - } - - switch (c) { - case 'h': - print_help(); - std::exit(0); - case 'f': - input_format = optarg; - break; - case 't': - output_format = optarg; - break; - default: - std::exit(1); - } - } - - const int remaining_args = argc - optind; - if (remaining_args == 0 || remaining_args > 2) { - std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]\n"; - std::exit(1); - } - - // Get input file name from command line. std::string input_file_name; - if (remaining_args >= 1) { - input_file_name = argv[optind]; - } - - // Get output file name from command line. std::string output_file_name; - if (remaining_args == 2) { - output_file_name = argv[optind+1]; + + for (int i = 1; i < argc; ++i) { + if (!std::strcmp(argv[i], "-f") || !std::strcmp(argv[i], "--from-format")) { + ++i; + if (i < argc) { + input_format = argv[i]; + } else { + print_usage(argv[0]); + } + } else if (!std::strncmp(argv[i], "--from-format=", 14)) { + input_format = argv[i] + 14; + } else if (!std::strcmp(argv[i], "-t") || !std::strcmp(argv[i], "--to-format")) { + ++i; + if (i < argc) { + output_format = argv[i]; + } else { + print_usage(argv[0]); + } + } else if (!std::strncmp(argv[i], "--to-format=", 12)) { + output_format = argv[i] + 12; + } else if (input_file_name.empty()) { + input_file_name = argv[i]; + } else if (output_file_name.empty()) { + output_file_name = argv[i]; + } else { + print_usage(argv[0]); + } } // This declares the input and output files using either the suffix of diff --git a/third_party/libosmium/examples/osmium_count.cpp b/third_party/libosmium/examples/osmium_count.cpp index 7de1b6b7c6e..130b98a0ccc 100644 --- a/third_party/libosmium/examples/osmium_count.cpp +++ b/third_party/libosmium/examples/osmium_count.cpp @@ -43,17 +43,17 @@ struct CountHandler : public osmium::handler::Handler { std::uint64_t relations = 0; // This callback is called by osmium::apply for each node in the data. - void node(const osmium::Node&) noexcept { + void node(const osmium::Node& /*node*/) noexcept { ++nodes; } // This callback is called by osmium::apply for each way in the data. - void way(const osmium::Way&) noexcept { + void way(const osmium::Way& /*way*/) noexcept { ++ways; } // This callback is called by osmium::apply for each relation in the data. - void relation(const osmium::Relation&) noexcept { + void relation(const osmium::Relation& /*relation*/) noexcept { ++relations; } diff --git a/third_party/libosmium/examples/osmium_create_pois.cpp b/third_party/libosmium/examples/osmium_create_pois.cpp index df27bfeff7b..dedbdb88aa2 100644 --- a/third_party/libosmium/examples/osmium_create_pois.cpp +++ b/third_party/libosmium/examples/osmium_create_pois.cpp @@ -31,11 +31,8 @@ #include // We want to use the builder interface -#include #include - -// Declare this to use the functions starting with the underscore (_) below. -using namespace osmium::builder::attr; +#include int main(int argc, char* argv[]) { if (argc != 2) { @@ -56,6 +53,9 @@ int main(int argc, char* argv[]) { const size_t initial_buffer_size = 10000; osmium::memory::Buffer buffer{initial_buffer_size, osmium::memory::Buffer::auto_grow::yes}; + // Declare this to use the functions starting with the underscore (_) below. + using namespace osmium::builder::attr; // NOLINT(google-build-using-namespace) + // Add nodes to the buffer. This is, of course, only an example. // You can set any of the attributes and more tags, etc. Ways and // relations can be added in a similar way. diff --git a/third_party/libosmium/examples/osmium_dump_internal.cpp b/third_party/libosmium/examples/osmium_dump_internal.cpp index 78229dadcd5..39523fa5472 100644 --- a/third_party/libosmium/examples/osmium_dump_internal.cpp +++ b/third_party/libosmium/examples/osmium_dump_internal.cpp @@ -29,8 +29,8 @@ */ #include // for errno -#include // for std::strerror #include // for std::exit +#include // for std::strerror #include // for std::cout, std::cerr #include // for std::string #include // for open @@ -71,7 +71,7 @@ class IndexFile { public: explicit IndexFile(const std::string& filename) : - m_fd(::open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666)) { + m_fd(::open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666)) { // NOLINT(hicpp-signed-bitwise) if (m_fd < 0) { std::cerr << "Can't open index file '" << filename << "': " << std::strerror(errno) << "\n"; std::exit(2); @@ -81,6 +81,12 @@ class IndexFile { #endif } + IndexFile(const IndexFile&) = delete; + IndexFile& operator=(const IndexFile&) = delete; + + IndexFile(IndexFile&&) = delete; + IndexFile& operator=(IndexFile&&) = delete; + ~IndexFile() { if (m_fd >= 0) { close(m_fd); @@ -115,7 +121,7 @@ int main(int argc, char* argv[]) { // Create the output file which will contain our serialized OSM data const std::string data_file{output_dir + "/data.osm.ser"}; - const int data_fd = ::open(data_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); + const int data_fd = ::open(data_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); // NOLINT(hicpp-signed-bitwise) if (data_fd < 0) { std::cerr << "Can't open data file '" << data_file << "': " << std::strerror(errno) << "\n"; std::exit(2); diff --git a/third_party/libosmium/examples/osmium_filter_discussions.cpp b/third_party/libosmium/examples/osmium_filter_discussions.cpp index c2a94e5a272..c4d092a0a38 100644 --- a/third_party/libosmium/examples/osmium_filter_discussions.cpp +++ b/third_party/libosmium/examples/osmium_filter_discussions.cpp @@ -3,7 +3,7 @@ EXAMPLE osmium_filter_discussions Read OSM changesets with discussions from a changeset dump like the one - you get from http://planet.osm.org/planet/discussions-latest.osm.bz2 + you get from https://planet.osm.org/planet/discussions-latest.osm.bz2 and write out only those changesets which have discussions (ie comments). DEMONSTRATES USE OF: diff --git a/third_party/libosmium/examples/osmium_index_lookup.cpp b/third_party/libosmium/examples/osmium_index_lookup.cpp index 2af2378339d..2f428bf7cd5 100644 --- a/third_party/libosmium/examples/osmium_index_lookup.cpp +++ b/third_party/libosmium/examples/osmium_index_lookup.cpp @@ -24,8 +24,8 @@ #include // for std::all_of, std::equal_range #include // for std::exit +#include // for std::strcmp #include // for open -#include // for getopt_long #include // for std::cout, std::cerr #include // for std::unique_ptr #include // for std::string @@ -64,6 +64,12 @@ class IndexAccess { return m_fd; } + IndexAccess(const IndexAccess&) = delete; + IndexAccess& operator=(const IndexAccess&) = delete; + + IndexAccess(IndexAccess&&) = delete; + IndexAccess& operator=(IndexAccess&&) = delete; + virtual ~IndexAccess() = default; virtual void dump() const = 0; @@ -83,7 +89,7 @@ class IndexAccess { template class IndexAccessDense : public IndexAccess { - using index_type = typename osmium::index::map::DenseFileArray; + using index_type = typename osmium::index::map::DenseFileArray; public: @@ -187,58 +193,70 @@ class Options { ; } + void print_usage(const char* prgname) { + std::cout << "Usage: " << prgname << " [OPTIONS]\n\n"; + std::exit(0); + } + public: Options(int argc, char* argv[]) { if (argc == 1) { - print_help(); - std::exit(1); + print_usage(argv[0]); } - static struct option long_options[] = { - {"array", required_argument, nullptr, 'a'}, - {"dump", no_argument, nullptr, 'd'}, - {"help", no_argument, nullptr, 'h'}, - {"list", required_argument, nullptr, 'l'}, - {"search", required_argument, nullptr, 's'}, - {"type", required_argument, nullptr, 't'}, - {nullptr, 0, nullptr, 0} - }; - - while (true) { - const int c = getopt_long(argc, argv, "a:dhl:s:t:", long_options, nullptr); - if (c == -1) { - break; - } + if (argc > 1 && (!std::strcmp(argv[1], "-h") || + !std::strcmp(argv[1], "--help"))) { + print_help(); + std::exit(0); + } - switch (c) { - case 'a': + for (int i = 1; i < argc; ++i) { + if (!std::strcmp(argv[i], "-a") || !std::strcmp(argv[i], "--array")) { + ++i; + if (i < argc) { m_array_format = true; - m_filename = optarg; - break; - case 'd': - m_dump = true; - break; - case 'h': - print_help(); - std::exit(0); - case 'l': + m_filename = argv[i]; + } else { + print_usage(argv[0]); + } + } else if (!std::strncmp(argv[i], "--array=", 8)) { + m_array_format = true; + m_filename = argv[i] + 8; + } else if (!std::strcmp(argv[i], "-l") || !std::strcmp(argv[i], "--list")) { + ++i; + if (i < argc) { m_list_format = true; - m_filename = optarg; - break; - case 's': - m_ids.push_back(std::atoll(optarg)); - break; - case 't': - m_type = optarg; - if (m_type != "location" && m_type != "id" && m_type != "offset") { - std::cerr << "Unknown type '" << m_type - << "'. Must be 'location', 'id', or 'offset'.\n"; - std::exit(2); - } - break; - default: - std::exit(2); + m_filename = argv[i]; + } else { + print_usage(argv[0]); + } + } else if (!std::strncmp(argv[i], "--list=", 7)) { + m_list_format = true; + m_filename = argv[i] + 7; + } else if (!std::strcmp(argv[i], "-d") || !std::strcmp(argv[i], "--dump")) { + m_dump = true; + } else if (!std::strcmp(argv[i], "-s") || !std::strcmp(argv[i], "--search")) { + ++i; + if (i < argc) { + m_ids.push_back(std::atoll(argv[i])); // NOLINT(cert-err34-c) + } else { + print_usage(argv[0]); + } + } else if (!std::strncmp(argv[i], "--search=", 9)) { + m_ids.push_back(std::atoll(argv[i] + 9)); // NOLINT(cert-err34-c) + } else if (!std::strcmp(argv[i], "-t") || !std::strcmp(argv[i], "--type")) { + ++i; + if (i < argc) { + m_type = argv[i]; + } else { + print_usage(argv[0]); + } + } else if (!std::strncmp(argv[i], "--type=", 7)) { + m_type = argv[i] + 7; + } else { + std::cerr << "Unknown command line options or arguments\n"; + print_usage(argv[0]); } } @@ -257,6 +275,11 @@ class Options { std::exit(2); } + if (m_type != "location" && m_type != "id" && m_type != "offset") { + std::cerr << "Unknown type '" << m_type + << "'. Must be 'location', 'id', or 'offset'.\n"; + std::exit(2); + } } const char* filename() const noexcept { @@ -302,9 +325,8 @@ int run(const IndexAccess& index, const Options& options) { if (options.do_dump()) { index.dump(); return 0; - } else { - return index.search(options.search_keys()) ? 0 : 1; } + return index.search(options.search_keys()) ? 0 : 1; } int main(int argc, char* argv[]) { @@ -329,16 +351,18 @@ int main(int argc, char* argv[]) { // index id -> location const auto index = create(options.dense_format(), fd); return run(*index, options); - } else if (options.type_is("id")) { + } + + if (options.type_is("id")) { // index id -> id const auto index = create(options.dense_format(), fd); return run(*index, options); - } else { - // index id -> offset - const auto index = create(options.dense_format(), fd); - return run(*index, options); } - } catch(const std::exception& e) { + + // index id -> offset + const auto index = create(options.dense_format(), fd); + return run(*index, options); + } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << '\n'; std::exit(1); } diff --git a/third_party/libosmium/examples/osmium_location_cache_create.cpp b/third_party/libosmium/examples/osmium_location_cache_create.cpp index a7119df6048..d1b381082e3 100644 --- a/third_party/libosmium/examples/osmium_location_cache_create.cpp +++ b/third_party/libosmium/examples/osmium_location_cache_create.cpp @@ -42,8 +42,8 @@ // For the location index. There are different types of index implementation // available. These implementations put the index on disk. See below. -#include #include +#include // For the NodeLocationForWays handler #include @@ -72,7 +72,7 @@ int main(int argc, char* argv[]) { osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::node}; // Initialize location index on disk creating a new file. - const int fd = ::open(cache_filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666); + const int fd = ::open(cache_filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666); // NOLINT(hicpp-signed-bitwise) if (fd == -1) { std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n"; std::exit(1); diff --git a/third_party/libosmium/examples/osmium_read_with_progress.cpp b/third_party/libosmium/examples/osmium_read_with_progress.cpp index 81437a665df..415e78de886 100644 --- a/third_party/libosmium/examples/osmium_read_with_progress.cpp +++ b/third_party/libosmium/examples/osmium_read_with_progress.cpp @@ -38,7 +38,7 @@ int main(int argc, char* argv[]) { osmium::io::Reader reader{input_file}; // Initialize progress bar, enable it only if STDERR is a TTY. - osmium::ProgressBar progress{reader.file_size(), osmium::util::isatty(2)}; + osmium::ProgressBar progress{reader.file_size(), osmium::isatty(2)}; // OSM data comes in buffers, read until there are no more. while (osmium::memory::Buffer buffer = reader.read()) { diff --git a/third_party/libosmium/examples/osmium_tiles.cpp b/third_party/libosmium/examples/osmium_tiles.cpp index 7dbeb3e29ae..52a1a1f5d91 100644 --- a/third_party/libosmium/examples/osmium_tiles.cpp +++ b/third_party/libosmium/examples/osmium_tiles.cpp @@ -36,21 +36,21 @@ int main(int argc, char* argv[]) { std::exit(1); } - const int zoom = std::atoi(argv[1]); + const int zoom = std::atoi(argv[1]); // NOLINT(cert-err34-c) if (zoom < 0 || zoom > 30) { std::cerr << "ERROR: Zoom must be between 0 and 30\n"; std::exit(1); } - const double lon = std::atof(argv[2]); - const double lat = std::atof(argv[3]); - - // Create location from WGS84 coordinates. In Osmium the order of - // coordinate values is always x/longitude first, then y/latitude. - const osmium::Location location{lon, lat}; - - std::cout << "WGS84: lon=" << lon << " lat=" << lat << "\n"; + osmium::Location location{}; + try { + location.set_lon(argv[2]); + location.set_lat(argv[3]); + } catch (const osmium::invalid_location&) { + std::cerr << "ERROR: Location is invalid\n"; + std::exit(1); + } // A location can store some invalid locations, ie locations outside the // -180 to 180 and -90 to 90 degree range. This function checks for that. @@ -59,6 +59,8 @@ int main(int argc, char* argv[]) { std::exit(1); } + std::cout << "WGS84: lon=" << location.lon() << " lat=" << location.lat() << "\n"; + // Project the coordinates using a helper function. You can also use the // osmium::geom::MercatorProjection class. const osmium::geom::Coordinates c = osmium::geom::lonlat_to_mercator(location); diff --git a/third_party/libosmium/include/osmium/area/assembler.hpp b/third_party/libosmium/include/osmium/area/assembler.hpp index 461a1837613..29fd6a5968b 100644 --- a/third_party/libosmium/include/osmium/area/assembler.hpp +++ b/third_party/libosmium/include/osmium/area/assembler.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,10 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include -#include - #include #include #include @@ -50,6 +46,10 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include +#include + namespace osmium { namespace area { @@ -111,8 +111,6 @@ namespace osmium { detail::BasicAssemblerWithTags(config) { } - ~Assembler() noexcept = default; - /** * Assemble an area from the given way. * The resulting area is put into the out_buffer. diff --git a/third_party/libosmium/include/osmium/area/assembler_config.hpp b/third_party/libosmium/include/osmium/area/assembler_config.hpp index 24bd60a99d2..fce53badb0a 100644 --- a/third_party/libosmium/include/osmium/area/assembler_config.hpp +++ b/third_party/libosmium/include/osmium/area/assembler_config.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/area/assembler_legacy.hpp b/third_party/libosmium/include/osmium/area/assembler_legacy.hpp index e616c947cb1..2531ad863d7 100644 --- a/third_party/libosmium/include/osmium/area/assembler_legacy.hpp +++ b/third_party/libosmium/include/osmium/area/assembler_legacy.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,18 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include #include @@ -62,6 +50,18 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + namespace osmium { namespace area { @@ -207,8 +207,6 @@ namespace osmium { detail::BasicAssemblerWithTags(config) { } - ~AssemblerLegacy() noexcept = default; - /** * Assemble an area from the given way. * The resulting area is put into the out_buffer. @@ -335,7 +333,7 @@ namespace osmium { if (stats().wrong_role == 0) { detail::for_each_member(relation, members, [this, &ways_that_should_be_areas, &area_tags](const osmium::RelationMember& member, const osmium::Way& way) { if (!std::strcmp(member.role(), "inner")) { - if (!way.nodes().empty() && way.is_closed() && way.tags().size() > 0) { + if (!way.nodes().empty() && way.is_closed() && !way.tags().empty()) { const auto d = std::count_if(way.tags().cbegin(), way.tags().cend(), std::cref(filter())); if (d > 0) { osmium::tags::KeyFilter::iterator way_fi_begin(std::cref(filter()), way.tags().cbegin(), way.tags().cend()); diff --git a/third_party/libosmium/include/osmium/area/detail/basic_assembler.hpp b/third_party/libosmium/include/osmium/area/detail/basic_assembler.hpp index 94168a12d5f..485d2223923 100644 --- a/third_party/libosmium/include/osmium/area/detail/basic_assembler.hpp +++ b/third_party/libosmium/include/osmium/area/detail/basic_assembler.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,6 +33,20 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include #include @@ -45,21 +59,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - namespace osmium { namespace area { @@ -70,19 +69,17 @@ namespace osmium { struct location_to_ring_map { osmium::Location location; - open_ring_its_type::iterator ring_it; - bool start; + open_ring_its_type::iterator ring_it{}; + bool start{false}; - location_to_ring_map(const osmium::Location& l, const open_ring_its_type::iterator& r, bool s) noexcept : + location_to_ring_map(osmium::Location l, open_ring_its_type::iterator r, const bool s) noexcept : location(l), ring_it(r), start(s) { } - explicit location_to_ring_map(const osmium::Location& l) noexcept : - location(l), - ring_it(), - start(false) { + explicit location_to_ring_map(osmium::Location l) noexcept : + location(l) { } const ProtoRing& ring() const noexcept { @@ -108,7 +105,7 @@ namespace osmium { struct slocation { - static constexpr const uint32_t invalid_item = 1 << 30; + static constexpr const uint32_t invalid_item = 1u << 30u; uint32_t item : 31; uint32_t reverse : 1; @@ -320,7 +317,7 @@ namespace osmium { const int64_t ly = end_location.y(); const auto z = (bx - ax)*(ly - ay) - (by - ay)*(lx - ax); if (debug()) { - std::cerr << " Segment XXXX z=" << z << "\n"; + std::cerr << " Segment z=" << z << '\n'; } if (z > 0) { nesting += segment->is_reverse() ? -1 : 1; @@ -623,8 +620,8 @@ namespace osmium { } void merge_two_rings(open_ring_its_type& open_ring_its, const location_to_ring_map& m1, const location_to_ring_map& m2) { - std::list::iterator r1 = *m1.ring_it; - std::list::iterator r2 = *m2.ring_it; + const auto r1 = *m1.ring_it; + const auto r2 = *m2.ring_it; if (r1->get_node_ref_stop().location() == r2->get_node_ref_start().location()) { r1->join_forward(*r2); @@ -689,13 +686,12 @@ namespace osmium { struct candidate { int64_t sum; - std::vector> rings; + std::vector> rings{}; osmium::Location start_location; osmium::Location stop_location; explicit candidate(location_to_ring_map& ring, bool reverse) : sum(ring.ring().sum()), - rings(), start_location(ring.ring().get_node_ref_start().location()), stop_location(ring.ring().get_node_ref_stop().location()) { rings.emplace_back(ring, reverse); @@ -813,8 +809,8 @@ namespace osmium { ++m_stats.open_rings; if (m_config.problem_reporter) { for (auto& it : open_ring_its) { - m_config.problem_reporter->report_ring_not_closed(it->get_node_ref_start()); - m_config.problem_reporter->report_ring_not_closed(it->get_node_ref_stop()); + m_config.problem_reporter->report_ring_not_closed(it->get_node_ref_start(), nullptr); + m_config.problem_reporter->report_ring_not_closed(it->get_node_ref_stop(), nullptr); } } } @@ -939,20 +935,6 @@ namespace osmium { return true; } - /** - * Checks if any ways were completely removed in the - * erase_duplicate_segments step. - */ - bool ways_were_lost() { - std::unordered_set ways_in_segments; - - for (const auto& segment : m_segment_list) { - ways_in_segments.insert(segment.way()); - } - - return ways_in_segments.size() < m_num_members; - } - #ifdef OSMIUM_WITH_TIMER static bool print_header() { std::cout << "nodes outer_rings inner_rings sort dupl intersection locations split simple_case complex_case roles_check\n"; @@ -1022,15 +1004,6 @@ namespace osmium { return false; } - // If one or more complete ways was removed because of - // duplicate segments, this isn't a valid area. - if (ways_were_lost()) { - if (debug()) { - std::cerr << " Complete ways removed because of duplicate segments\n"; - } - return false; - } - if (m_config.debug_level >= 3) { std::cerr << "Sorted de-duplicated segment list:\n"; for (const auto& s : m_segment_list) { @@ -1170,8 +1143,6 @@ namespace osmium { #endif } - ~BasicAssembler() noexcept = default; - const AssemblerConfig& config() const noexcept { return m_config; } diff --git a/third_party/libosmium/include/osmium/area/detail/basic_assembler_with_tags.hpp b/third_party/libosmium/include/osmium/area/detail/basic_assembler_with_tags.hpp index 1e946805cfe..821e914de08 100644 --- a/third_party/libosmium/include/osmium/area/detail/basic_assembler_with_tags.hpp +++ b/third_party/libosmium/include/osmium/area/detail/basic_assembler_with_tags.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,14 +33,14 @@ DEALINGS IN THE SOFTWARE. */ -#include - #include #include #include #include #include +#include + namespace osmium { namespace area { @@ -68,7 +68,7 @@ namespace osmium { static void copy_tags_without_type(osmium::builder::AreaBuilder& builder, const osmium::TagList& tags) { osmium::builder::TagListBuilder tl_builder{builder}; for (const osmium::Tag& tag : tags) { - if (std::strcmp(tag.key(), "type")) { + if (std::strcmp(tag.key(), "type") != 0) { tl_builder.add_tag(tag.key(), tag.value()); } } diff --git a/third_party/libosmium/include/osmium/area/detail/node_ref_segment.hpp b/third_party/libosmium/include/osmium/area/detail/node_ref_segment.hpp index 8039579b2e6..dc3782dbee1 100644 --- a/third_party/libosmium/include/osmium/area/detail/node_ref_segment.hpp +++ b/third_party/libosmium/include/osmium/area/detail/node_ref_segment.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,16 +33,16 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include +#include + #include #include #include #include #include -#include -#include -#include - namespace osmium { class Way; @@ -74,20 +74,20 @@ namespace osmium { class NodeRefSegment { // First node in order described above. - osmium::NodeRef m_first; + osmium::NodeRef m_first{}; // Second node in order described above. - osmium::NodeRef m_second; + osmium::NodeRef m_second{}; // Way this segment was from. - const osmium::Way* m_way; + const osmium::Way* m_way = nullptr; // The ring this segment is part of. Initially nullptr, this // will be filled in once we know which ring the segment is in. - ProtoRing* m_ring; + ProtoRing* m_ring = nullptr; // The role of this segment from the member role. - role_type m_role; + role_type m_role = role_type::unknown; // Nodes have to be reversed to get the intended order. bool m_reverse = false; @@ -98,19 +98,12 @@ namespace osmium { public: - NodeRefSegment() noexcept : - m_first(), - m_second(), - m_way(nullptr), - m_ring(nullptr), - m_role(role_type::unknown) { - } + NodeRefSegment() noexcept = default; NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, role_type role, const osmium::Way* way) noexcept : m_first(nr1), m_second(nr2), m_way(way), - m_ring(nullptr), m_role(role) { if (nr2.location() < nr1.location()) { using std::swap; @@ -303,7 +296,7 @@ namespace osmium { * or a defined Location if the segments intersect. */ inline osmium::Location calculate_intersection(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept { - // See http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect + // See https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect // for some hints about how the algorithm works. const vec p0{s1.first()}; const vec p1{s1.second()}; diff --git a/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp b/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp index 7ab353b882c..6be29f94d72 100644 --- a/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp +++ b/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,6 +33,10 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include +#include + #include #include #include @@ -40,10 +44,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include -#include -#include - namespace osmium { class Way; @@ -64,18 +64,18 @@ namespace osmium { private: // Segments in this ring. - segments_type m_segments; + segments_type m_segments{}; // If this is an outer ring, these point to it's inner rings // (if any). - std::vector m_inner; + std::vector m_inner{}; // The smallest segment. Will be kept current whenever a new // segment is added to the ring. NodeRefSegment* m_min_segment; // If this is an inner ring, points to the outer ring. - ProtoRing* m_outer_ring; + ProtoRing* m_outer_ring = nullptr; #ifdef OSMIUM_DEBUG_RING_NO static int64_t next_num() noexcept { @@ -91,10 +91,7 @@ namespace osmium { public: explicit ProtoRing(NodeRefSegment* segment) noexcept : - m_segments(), - m_inner(), m_min_segment(segment), - m_outer_ring(nullptr), #ifdef OSMIUM_DEBUG_RING_NO m_num(next_num()), #endif diff --git a/third_party/libosmium/include/osmium/area/detail/segment_list.hpp b/third_party/libosmium/include/osmium/area/detail/segment_list.hpp index 39e16abaeaf..776507b46a9 100644 --- a/third_party/libosmium/include/osmium/area/detail/segment_list.hpp +++ b/third_party/libosmium/include/osmium/area/detail/segment_list.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,6 +33,15 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include +#include +#include +#include +#include +#include +#include + #include #include #include @@ -43,15 +52,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include -#include -#include -#include -#include -#include -#include -#include - namespace osmium { namespace area { @@ -84,7 +84,7 @@ namespace osmium { using slist_type = std::vector; - slist_type m_segments; + slist_type m_segments{}; bool m_debug; @@ -144,7 +144,6 @@ namespace osmium { public: explicit SegmentList(bool debug) noexcept : - m_segments(), m_debug(debug) { } diff --git a/third_party/libosmium/include/osmium/area/detail/vector.hpp b/third_party/libosmium/include/osmium/area/detail/vector.hpp index e8ff0a501ff..dae6c634739 100644 --- a/third_party/libosmium/include/osmium/area/detail/vector.hpp +++ b/third_party/libosmium/include/osmium/area/detail/vector.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,12 +33,12 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include - #include #include +#include +#include + namespace osmium { namespace area { diff --git a/third_party/libosmium/include/osmium/area/geom_assembler.hpp b/third_party/libosmium/include/osmium/area/geom_assembler.hpp index 8fb3b3ac5dc..4c9fc276ea1 100644 --- a/third_party/libosmium/include/osmium/area/geom_assembler.hpp +++ b/third_party/libosmium/include/osmium/area/geom_assembler.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp b/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp index 9ad68e166d2..d57294120fd 100644 --- a/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp +++ b/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,11 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include -#include -#include - #include #include #include @@ -48,6 +43,11 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include +#include +#include + namespace osmium { namespace relations { diff --git a/third_party/libosmium/include/osmium/area/multipolygon_manager.hpp b/third_party/libosmium/include/osmium/area/multipolygon_manager.hpp index e5ee2c8f196..dd3c4d875b7 100644 --- a/third_party/libosmium/include/osmium/area/multipolygon_manager.hpp +++ b/third_party/libosmium/include/osmium/area/multipolygon_manager.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,13 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include -#include -#include -#include -#include - #include #include #include @@ -53,6 +46,13 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include +#include +#include +#include +#include + namespace osmium { /** @@ -93,9 +93,9 @@ namespace osmium { * needed on closed ways or multipolygon relations * to build the area. */ - explicit MultipolygonManager(const assembler_config_type& assembler_config, const osmium::TagsFilter& filter = osmium::TagsFilter{true}) : - m_assembler_config(assembler_config), - m_filter(filter) { + explicit MultipolygonManager(assembler_config_type assembler_config, osmium::TagsFilter filter = osmium::TagsFilter{true}) : + m_assembler_config(std::move(assembler_config)), + m_filter(std::move(filter)) { } /** diff --git a/third_party/libosmium/include/osmium/area/multipolygon_manager_legacy.hpp b/third_party/libosmium/include/osmium/area/multipolygon_manager_legacy.hpp index 491557aa8f2..c611d3447f7 100644 --- a/third_party/libosmium/include/osmium/area/multipolygon_manager_legacy.hpp +++ b/third_party/libosmium/include/osmium/area/multipolygon_manager_legacy.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,13 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include -#include -#include -#include -#include - #include #include #include @@ -55,6 +48,13 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include +#include +#include +#include +#include + namespace osmium { /** diff --git a/third_party/libosmium/include/osmium/area/problem_reporter.hpp b/third_party/libosmium/include/osmium/area/problem_reporter.hpp index 181d2d1ee64..36f0833fae8 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,12 +33,12 @@ DEALINGS IN THE SOFTWARE. */ -#include - #include #include #include +#include + namespace osmium { class NodeRef; @@ -62,18 +62,24 @@ namespace osmium { protected: // Type of object we are currently working on - osmium::item_type m_object_type; + osmium::item_type m_object_type = osmium::item_type::undefined; // ID of the relation/way we are currently working on - osmium::object_id_type m_object_id; + osmium::object_id_type m_object_id = 0; // Number of nodes in the area - size_t m_nodes; + size_t m_nodes = 0; public: ProblemReporter() = default; + ProblemReporter(const ProblemReporter&) = default; + ProblemReporter& operator=(const ProblemReporter&) = default; + + ProblemReporter(ProblemReporter&&) noexcept = default; + ProblemReporter& operator=(ProblemReporter&&) noexcept = default; + virtual ~ProblemReporter() = default; /** @@ -161,7 +167,7 @@ namespace osmium { * @param nr NodeRef of one end of the ring. * @param way Optional pointer to way the end node is in. */ - virtual void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) { + virtual void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way) { } /** diff --git a/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp b/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp index cfc2b5428c5..2224f66eaae 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,13 +33,13 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include - #include #include #include +#include +#include + namespace osmium { class NodeRef; @@ -57,8 +57,6 @@ namespace osmium { ProblemReporterStream(m_sstream) { } - ~ProblemReporterException() override = default; - void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override { m_sstream.str(""); ProblemReporterStream::report_duplicate_node(node_id1, node_id2, location); @@ -90,7 +88,7 @@ namespace osmium { throw std::runtime_error{m_sstream.str()}; } - void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override { + void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way) override { m_sstream.str(""); ProblemReporterStream::report_ring_not_closed(nr, way); throw std::runtime_error{m_sstream.str()}; diff --git a/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp b/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp index 5f531c8f4f3..748cf059c54 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -42,10 +42,6 @@ DEALINGS IN THE SOFTWARE. * @attention If you include this file, you'll need to link with `libgdal`. */ -#include - -#include - #include #include #include @@ -56,6 +52,10 @@ DEALINGS IN THE SOFTWARE. #include #include +#include + +#include + namespace osmium { namespace area { @@ -137,8 +137,6 @@ namespace osmium { ; } - ~ProblemReporterOGR() override = default; - void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override { write_point("duplicate_node", node_id1, node_id2, location); } @@ -162,7 +160,7 @@ namespace osmium { write_line("overlapping_segment", nr1.ref(), nr2.ref(), nr1.location(), nr2.location()); } - void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override { + void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way) override { write_point("ring_not_closed", nr.ref(), way ? way->id() : 0, nr.location()); } diff --git a/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp b/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp index 774e9c3b477..09c48d5dc3d 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,8 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include - #include #include #include @@ -42,6 +40,8 @@ DEALINGS IN THE SOFTWARE. #include #include +#include + namespace osmium { namespace area { @@ -56,8 +56,6 @@ namespace osmium { m_out(&out) { } - ~ProblemReporterStream() override = default; - void header(const char* msg) { *m_out << "DATA PROBLEM: " << msg << " on " << item_type_to_char(m_object_type) << m_object_id << " (with " << m_nodes << " nodes): "; } @@ -91,7 +89,7 @@ namespace osmium { << " node_id2=" << nr2.ref() << " location2=" << nr2.location() << "\n"; } - void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override { + void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way) override { header("ring not closed"); *m_out << "node_id=" << nr.ref() << " location=" << nr.location(); if (way) { diff --git a/third_party/libosmium/include/osmium/area/stats.hpp b/third_party/libosmium/include/osmium/area/stats.hpp index 6133da8473b..7804111817a 100644 --- a/third_party/libosmium/include/osmium/area/stats.hpp +++ b/third_party/libosmium/include/osmium/area/stats.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/third_party/libosmium/include/osmium/builder/attr.hpp b/third_party/libosmium/include/osmium/builder/attr.hpp index bcd48613a62..2dd6239ea88 100644 --- a/third_party/libosmium/include/osmium/builder/attr.hpp +++ b/third_party/libosmium/include/osmium/builder/attr.hpp @@ -5,7 +5,7 @@ This file is part of Osmium (http://osmcode.org/libosmium). -Copyright 2013-2017 Jochen Topf and others (see README). +Copyright 2013-2018 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,16 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include #include @@ -56,6 +46,16 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + namespace osmium { namespace builder { @@ -94,13 +94,13 @@ namespace osmium { }; #else // True if Predicate matches for none of the types Ts - template class Predicate, typename... Ts> + template