Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 17e19663ba | |||
| 5597415f28 | |||
| 5476f6ab27 | |||
| 0971f06193 |
@@ -13,6 +13,7 @@ notifications:
|
|||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
- "5.19"
|
||||||
# enable building tags
|
# enable building tags
|
||||||
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||||
|
|
||||||
|
|||||||
+8
-3
@@ -1,11 +1,16 @@
|
|||||||
# UNRELEASED
|
# 5.19.0
|
||||||
- Changes from 5.18.0:
|
- Changes from 5.18.0:
|
||||||
- Optimizations:
|
- Optimizations:
|
||||||
- CHANGED: Map matching is now almost twice as fast. [#5060](https://github.com/Project-OSRM/osrm-backend/pull/5060)
|
|
||||||
- CHANGED: Use Grisu2 for serializing floating point numbers. [#5188](https://github.com/Project-OSRM/osrm-backend/pull/5188)
|
- CHANGED: Use Grisu2 for serializing floating point numbers. [#5188](https://github.com/Project-OSRM/osrm-backend/pull/5188)
|
||||||
|
- ADDED: Node bindings can return pre-rendered JSON buffer. [#5189](https://github.com/Project-OSRM/osrm-backend/pull/5189)
|
||||||
|
- Profiles:
|
||||||
|
- CHANGED: Bicycle profile now blacklists barriers instead of whitelisting them [#5076
|
||||||
|
](https://github.com/Project-OSRM/osrm-backend/pull/5076/)
|
||||||
|
- CHANGED: Foot profile now blacklists barriers instead of whitelisting them [#5077
|
||||||
|
](https://github.com/Project-OSRM/osrm-backend/pull/5077/)
|
||||||
|
- CHANGED: Support maxlength and maxweight in car profile [#5101](https://github.com/Project-OSRM/osrm-backend/pull/5101]
|
||||||
- Bugfixes:
|
- Bugfixes:
|
||||||
- FIXED: collapsing of ExitRoundabout instructions [#5114](https://github.com/Project-OSRM/osrm-backend/issues/5114)
|
- FIXED: collapsing of ExitRoundabout instructions [#5114](https://github.com/Project-OSRM/osrm-backend/issues/5114)
|
||||||
- FIXED: negative distances in table plugin annotation [#5106](https://github.com/Project-OSRM/osrm-backend/issues/5106)
|
|
||||||
- Misc:
|
- Misc:
|
||||||
- CHANGED: Support up to 512 named shared memory regions [#5185](https://github.com/Project-OSRM/osrm-backend/pull/5185)
|
- CHANGED: Support up to 512 named shared memory regions [#5185](https://github.com/Project-OSRM/osrm-backend/pull/5185)
|
||||||
|
|
||||||
|
|||||||
@@ -297,6 +297,29 @@ Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refer
|
|||||||
2) `waypoint_index`: index of the point in the trip.
|
2) `waypoint_index`: index of the point in the trip.
|
||||||
**`trips`**: an array of [`Route`](#route) objects that assemble the trace.
|
**`trips`**: an array of [`Route`](#route) objects that assemble the trace.
|
||||||
|
|
||||||
|
## Plugin behaviour
|
||||||
|
|
||||||
|
All plugins support a second additional object that is available to configure some NodeJS specific behaviours.
|
||||||
|
|
||||||
|
- `plugin_config` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object literal containing parameters for the trip query.
|
||||||
|
- `plugin_config.format` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** The format of the result object to various API calls. Valid options are `object` (default), which returns a standard Javascript object, as described above, and `json_buffer`, which will return a NodeJS **[Buffer](https://nodejs.org/api/buffer.html)** object, containing a JSON string. The latter has the advantage that it can be immediately serialized to disk/sent over the network, and the generation of the string is performed outside the main NodeJS event loop. This option is ignored by the `tile` plugin.
|
||||||
|
|
||||||
|
**Examples**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var osrm = new OSRM('network.osrm');
|
||||||
|
var options = {
|
||||||
|
coordinates: [
|
||||||
|
[13.36761474609375, 52.51663871100423],
|
||||||
|
[13.374481201171875, 52.506191342034576]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
osrm.route(options, { format: "json_buffer" }, function(err, response) {
|
||||||
|
if (err) throw err;
|
||||||
|
console.log(response.toString("utf-8"));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Responses
|
## Responses
|
||||||
|
|
||||||
Responses
|
Responses
|
||||||
|
|||||||
@@ -792,4 +792,4 @@ Feature: Basic Map Matching
|
|||||||
When I match I should get
|
When I match I should get
|
||||||
| trace | geometry | a:distance | a:duration | a:weight | duration |
|
| trace | geometry | a:distance | a:duration | a:weight | duration |
|
||||||
| 2345 | 1.00018,1,1.000315,1 | 15.013264 | 1.5 | 1.5 | 1.5 |
|
| 2345 | 1.00018,1,1.000315,1 | 15.013264 | 1.5 | 1.5 | 1.5 |
|
||||||
| 4321 | 1.00027,1,1.000135,1 | 15.013264 | 1.5 | 1.5 | 1.5 |
|
| 4321 | 1.00027,1,1.000135,1 | 15.013264 | 1.5 | 1.5 | 1.5 |
|
||||||
@@ -449,7 +449,6 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
|||||||
const auto reverse_durations = datafacade.GetUncompressedReverseDurations(geometry_id);
|
const auto reverse_durations = datafacade.GetUncompressedReverseDurations(geometry_id);
|
||||||
|
|
||||||
const auto forward_geometry = datafacade.GetUncompressedForwardGeometry(geometry_id);
|
const auto forward_geometry = datafacade.GetUncompressedForwardGeometry(geometry_id);
|
||||||
const auto reverse_geometry = datafacade.GetUncompressedReverseGeometry(geometry_id);
|
|
||||||
|
|
||||||
const auto forward_weight_offset =
|
const auto forward_weight_offset =
|
||||||
std::accumulate(forward_weights.begin(),
|
std::accumulate(forward_weights.begin(),
|
||||||
@@ -480,19 +479,19 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
|||||||
datafacade.GetCoordinateOfNode(forward_geometry(data.fwd_segment_position)),
|
datafacade.GetCoordinateOfNode(forward_geometry(data.fwd_segment_position)),
|
||||||
point_on_segment);
|
point_on_segment);
|
||||||
|
|
||||||
const auto rev_segment_position = reverse_weights.size() - data.fwd_segment_position - 1;
|
const auto reverse_weight_offset =
|
||||||
|
std::accumulate(reverse_weights.begin(),
|
||||||
const auto reverse_weight_offset = std::accumulate(
|
reverse_weights.end() - data.fwd_segment_position - 1,
|
||||||
reverse_weights.begin(), reverse_weights.begin() + rev_segment_position, EdgeWeight{0});
|
EdgeWeight{0});
|
||||||
|
|
||||||
const auto reverse_duration_offset =
|
const auto reverse_duration_offset =
|
||||||
std::accumulate(reverse_durations.begin(),
|
std::accumulate(reverse_durations.begin(),
|
||||||
reverse_durations.begin() + rev_segment_position,
|
reverse_durations.end() - data.fwd_segment_position - 1,
|
||||||
EdgeDuration{0});
|
EdgeDuration{0});
|
||||||
|
|
||||||
EdgeDistance reverse_distance_offset = 0;
|
EdgeDistance reverse_distance_offset = 0;
|
||||||
for (auto current = reverse_geometry.begin();
|
for (auto current = forward_geometry.begin();
|
||||||
current < reverse_geometry.begin() + rev_segment_position;
|
current < forward_geometry.end() - data.fwd_segment_position - 2;
|
||||||
++current)
|
++current)
|
||||||
{
|
{
|
||||||
reverse_distance_offset += util::coordinate_calculation::fccApproximateDistance(
|
reverse_distance_offset += util::coordinate_calculation::fccApproximateDistance(
|
||||||
@@ -500,11 +499,13 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
|||||||
datafacade.GetCoordinateOfNode(*std::next(current)));
|
datafacade.GetCoordinateOfNode(*std::next(current)));
|
||||||
}
|
}
|
||||||
|
|
||||||
EdgeWeight reverse_weight = reverse_weights[rev_segment_position];
|
EdgeWeight reverse_weight =
|
||||||
EdgeDuration reverse_duration = reverse_durations[rev_segment_position];
|
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(
|
EdgeDistance reverse_distance = util::coordinate_calculation::fccApproximateDistance(
|
||||||
point_on_segment,
|
point_on_segment,
|
||||||
datafacade.GetCoordinateOfNode(reverse_geometry(rev_segment_position)));
|
datafacade.GetCoordinateOfNode(forward_geometry(data.fwd_segment_position + 1)));
|
||||||
|
|
||||||
ratio = std::min(1.0, std::max(0.0, ratio));
|
ratio = std::min(1.0, std::max(0.0, ratio));
|
||||||
if (data.forward_segment_id.id != SPECIAL_SEGMENTID)
|
if (data.forward_segment_id.id != SPECIAL_SEGMENTID)
|
||||||
@@ -692,7 +693,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
|||||||
const CoordinateList &coordinates;
|
const CoordinateList &coordinates;
|
||||||
DataFacadeT &datafacade;
|
DataFacadeT &datafacade;
|
||||||
};
|
};
|
||||||
} // namespace engine
|
}
|
||||||
} // namespace osrm
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -44,19 +44,50 @@ bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &ta
|
|||||||
bool needsLoopForward(const PhantomNodes &phantoms);
|
bool needsLoopForward(const PhantomNodes &phantoms);
|
||||||
bool needsLoopBackwards(const PhantomNodes &phantoms);
|
bool needsLoopBackwards(const PhantomNodes &phantoms);
|
||||||
|
|
||||||
namespace detail
|
template <typename Heap>
|
||||||
|
void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNodes &nodes)
|
||||||
{
|
{
|
||||||
template <typename Algorithm>
|
const auto &source = nodes.source_phantom;
|
||||||
void insertSourceInHeap(typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &heap,
|
if (source.IsValidForwardSource())
|
||||||
const PhantomNode &phantom_node)
|
{
|
||||||
|
forward_heap.Insert(source.forward_segment_id.id,
|
||||||
|
-source.GetForwardWeightPlusOffset(),
|
||||||
|
source.forward_segment_id.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.IsValidReverseSource())
|
||||||
|
{
|
||||||
|
forward_heap.Insert(source.reverse_segment_id.id,
|
||||||
|
-source.GetReverseWeightPlusOffset(),
|
||||||
|
source.reverse_segment_id.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &target = nodes.target_phantom;
|
||||||
|
if (target.IsValidForwardTarget())
|
||||||
|
{
|
||||||
|
reverse_heap.Insert(target.forward_segment_id.id,
|
||||||
|
target.GetForwardWeightPlusOffset(),
|
||||||
|
target.forward_segment_id.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.IsValidReverseTarget())
|
||||||
|
{
|
||||||
|
reverse_heap.Insert(target.reverse_segment_id.id,
|
||||||
|
target.GetReverseWeightPlusOffset(),
|
||||||
|
target.reverse_segment_id.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ManyToManyQueryHeap>
|
||||||
|
void insertSourceInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_node)
|
||||||
{
|
{
|
||||||
if (phantom_node.IsValidForwardTarget())
|
if (phantom_node.IsValidForwardSource())
|
||||||
{
|
{
|
||||||
heap.Insert(phantom_node.forward_segment_id.id,
|
heap.Insert(phantom_node.forward_segment_id.id,
|
||||||
-phantom_node.GetForwardWeightPlusOffset(),
|
-phantom_node.GetForwardWeightPlusOffset(),
|
||||||
{phantom_node.forward_segment_id.id, -phantom_node.GetForwardDuration()});
|
{phantom_node.forward_segment_id.id, -phantom_node.GetForwardDuration()});
|
||||||
}
|
}
|
||||||
if (phantom_node.IsValidReverseTarget())
|
if (phantom_node.IsValidReverseSource())
|
||||||
{
|
{
|
||||||
heap.Insert(phantom_node.reverse_segment_id.id,
|
heap.Insert(phantom_node.reverse_segment_id.id,
|
||||||
-phantom_node.GetReverseWeightPlusOffset(),
|
-phantom_node.GetReverseWeightPlusOffset(),
|
||||||
@@ -64,9 +95,8 @@ void insertSourceInHeap(typename SearchEngineData<Algorithm>::ManyToManyQueryHea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Algorithm>
|
template <typename ManyToManyQueryHeap>
|
||||||
void insertTargetInHeap(typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &heap,
|
void insertTargetInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_node)
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
{
|
||||||
if (phantom_node.IsValidForwardTarget())
|
if (phantom_node.IsValidForwardTarget())
|
||||||
{
|
{
|
||||||
@@ -82,109 +112,6 @@ void insertTargetInHeap(typename SearchEngineData<Algorithm>::ManyToManyQueryHea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Algorithm>
|
|
||||||
void insertSourceInHeap(typename SearchEngineData<Algorithm>::QueryHeap &heap,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
if (phantom_node.IsValidForwardSource())
|
|
||||||
{
|
|
||||||
heap.Insert(phantom_node.forward_segment_id.id,
|
|
||||||
-phantom_node.GetForwardWeightPlusOffset(),
|
|
||||||
phantom_node.forward_segment_id.id);
|
|
||||||
}
|
|
||||||
if (phantom_node.IsValidReverseSource())
|
|
||||||
{
|
|
||||||
heap.Insert(phantom_node.reverse_segment_id.id,
|
|
||||||
-phantom_node.GetReverseWeightPlusOffset(),
|
|
||||||
phantom_node.reverse_segment_id.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Algorithm>
|
|
||||||
void insertTargetInHeap(typename SearchEngineData<Algorithm>::QueryHeap &heap,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
if (phantom_node.IsValidForwardTarget())
|
|
||||||
{
|
|
||||||
heap.Insert(phantom_node.forward_segment_id.id,
|
|
||||||
phantom_node.GetForwardWeightPlusOffset(),
|
|
||||||
phantom_node.forward_segment_id.id);
|
|
||||||
}
|
|
||||||
if (phantom_node.IsValidReverseTarget())
|
|
||||||
{
|
|
||||||
heap.Insert(phantom_node.reverse_segment_id.id,
|
|
||||||
phantom_node.GetReverseWeightPlusOffset(),
|
|
||||||
phantom_node.reverse_segment_id.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
inline void insertTargetInHeap(typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap &heap,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
detail::insertTargetInHeap<mld::Algorithm>(heap, phantom_node);
|
|
||||||
}
|
|
||||||
inline void insertTargetInHeap(typename SearchEngineData<ch::Algorithm>::ManyToManyQueryHeap &heap,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
detail::insertTargetInHeap<ch::Algorithm>(heap, phantom_node);
|
|
||||||
}
|
|
||||||
inline void insertTargetInHeap(typename SearchEngineData<mld::Algorithm>::QueryHeap &heap,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
detail::insertTargetInHeap<mld::Algorithm>(heap, phantom_node);
|
|
||||||
}
|
|
||||||
inline void insertTargetInHeap(typename SearchEngineData<ch::Algorithm>::QueryHeap &heap,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
detail::insertTargetInHeap<ch::Algorithm>(heap, phantom_node);
|
|
||||||
}
|
|
||||||
inline void insertSourceInHeap(typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap &heap,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
detail::insertSourceInHeap<mld::Algorithm>(heap, phantom_node);
|
|
||||||
}
|
|
||||||
inline void insertSourceInHeap(typename SearchEngineData<ch::Algorithm>::ManyToManyQueryHeap &heap,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
detail::insertSourceInHeap<ch::Algorithm>(heap, phantom_node);
|
|
||||||
}
|
|
||||||
inline void insertSourceInHeap(typename SearchEngineData<mld::Algorithm>::QueryHeap &heap,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
detail::insertSourceInHeap<mld::Algorithm>(heap, phantom_node);
|
|
||||||
}
|
|
||||||
inline void insertSourceInHeap(typename SearchEngineData<ch::Algorithm>::QueryHeap &heap,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
detail::insertSourceInHeap<ch::Algorithm>(heap, phantom_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Heap>
|
|
||||||
void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNodes &nodes)
|
|
||||||
{
|
|
||||||
insertSourceInHeap(forward_heap, nodes.source_phantom);
|
|
||||||
insertTargetInHeap(reverse_heap, nodes.target_phantom);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Algorithm>
|
|
||||||
void insertSourceInHeap(typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &heap,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
if (phantom_node.IsValidForwardSource())
|
|
||||||
{
|
|
||||||
heap.Insert(phantom_node.forward_segment_id.id,
|
|
||||||
-phantom_node.GetForwardWeightPlusOffset(),
|
|
||||||
{phantom_node.forward_segment_id.id, -phantom_node.GetForwardDuration()});
|
|
||||||
}
|
|
||||||
if (phantom_node.IsValidReverseSource())
|
|
||||||
{
|
|
||||||
heap.Insert(phantom_node.reverse_segment_id.id,
|
|
||||||
-phantom_node.GetReverseWeightPlusOffset(),
|
|
||||||
{phantom_node.reverse_segment_id.id, -phantom_node.GetReverseDuration()});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FacadeT>
|
template <typename FacadeT>
|
||||||
void annotatePath(const FacadeT &facade,
|
void annotatePath(const FacadeT &facade,
|
||||||
const PhantomNodes &phantom_node_pair,
|
const PhantomNodes &phantom_node_pair,
|
||||||
@@ -394,10 +321,58 @@ void annotatePath(const FacadeT &facade,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EdgeDistance adjustPathDistanceToPhantomNodes(const std::vector<NodeID> &path,
|
template <typename Algorithm>
|
||||||
const PhantomNode &source_phantom,
|
double getPathDistance(const DataFacade<Algorithm> &facade,
|
||||||
const PhantomNode &target_phantom,
|
const std::vector<PathData> unpacked_path,
|
||||||
const EdgeDistance distance);
|
const PhantomNode &source_phantom,
|
||||||
|
const PhantomNode &target_phantom)
|
||||||
|
{
|
||||||
|
using util::coordinate_calculation::detail::DEGREE_TO_RAD;
|
||||||
|
using util::coordinate_calculation::detail::EARTH_RADIUS;
|
||||||
|
|
||||||
|
double distance = 0;
|
||||||
|
double prev_lat =
|
||||||
|
static_cast<double>(util::toFloating(source_phantom.location.lat)) * DEGREE_TO_RAD;
|
||||||
|
double prev_lon =
|
||||||
|
static_cast<double>(util::toFloating(source_phantom.location.lon)) * DEGREE_TO_RAD;
|
||||||
|
double prev_cos = std::cos(prev_lat);
|
||||||
|
for (const auto &p : unpacked_path)
|
||||||
|
{
|
||||||
|
const auto current_coordinate = facade.GetCoordinateOfNode(p.turn_via_node);
|
||||||
|
|
||||||
|
const double current_lat =
|
||||||
|
static_cast<double>(util::toFloating(current_coordinate.lat)) * DEGREE_TO_RAD;
|
||||||
|
const double current_lon =
|
||||||
|
static_cast<double>(util::toFloating(current_coordinate.lon)) * DEGREE_TO_RAD;
|
||||||
|
const double current_cos = std::cos(current_lat);
|
||||||
|
|
||||||
|
const double sin_dlon = std::sin((prev_lon - current_lon) / 2.0);
|
||||||
|
const double sin_dlat = std::sin((prev_lat - current_lat) / 2.0);
|
||||||
|
|
||||||
|
const double aharv = sin_dlat * sin_dlat + prev_cos * current_cos * sin_dlon * sin_dlon;
|
||||||
|
const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv));
|
||||||
|
distance += EARTH_RADIUS * charv;
|
||||||
|
|
||||||
|
prev_lat = current_lat;
|
||||||
|
prev_lon = current_lon;
|
||||||
|
prev_cos = current_cos;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double current_lat =
|
||||||
|
static_cast<double>(util::toFloating(target_phantom.location.lat)) * DEGREE_TO_RAD;
|
||||||
|
const double current_lon =
|
||||||
|
static_cast<double>(util::toFloating(target_phantom.location.lon)) * DEGREE_TO_RAD;
|
||||||
|
const double current_cos = std::cos(current_lat);
|
||||||
|
|
||||||
|
const double sin_dlon = std::sin((prev_lon - current_lon) / 2.0);
|
||||||
|
const double sin_dlat = std::sin((prev_lat - current_lat) / 2.0);
|
||||||
|
|
||||||
|
const double aharv = sin_dlat * sin_dlat + prev_cos * current_cos * sin_dlon * sin_dlon;
|
||||||
|
const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv));
|
||||||
|
distance += EARTH_RADIUS * charv;
|
||||||
|
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename AlgorithmT>
|
template <typename AlgorithmT>
|
||||||
InternalRouteResult extractRoute(const DataFacade<AlgorithmT> &facade,
|
InternalRouteResult extractRoute(const DataFacade<AlgorithmT> &facade,
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
|||||||
const std::vector<std::size_t> &phantom_indices)
|
const std::vector<std::size_t> &phantom_indices)
|
||||||
{
|
{
|
||||||
auto min_level = [&partition, node](const PhantomNode &phantom_node) {
|
auto min_level = [&partition, node](const PhantomNode &phantom_node) {
|
||||||
|
|
||||||
const auto &forward_segment = phantom_node.forward_segment_id;
|
const auto &forward_segment = phantom_node.forward_segment_id;
|
||||||
const auto forward_level =
|
const auto forward_level =
|
||||||
forward_segment.enabled ? partition.GetHighestDifferentLevel(node, forward_segment.id)
|
forward_segment.enabled ? partition.GetHighestDifferentLevel(node, forward_segment.id)
|
||||||
@@ -119,7 +120,7 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} // namespace
|
}
|
||||||
|
|
||||||
// Heaps only record for each node its predecessor ("parent") on the shortest path.
|
// Heaps only record for each node its predecessor ("parent") on the shortest path.
|
||||||
// For re-constructing the actual path we need to trace back all parent "pointers".
|
// For re-constructing the actual path we need to trace back all parent "pointers".
|
||||||
@@ -390,27 +391,21 @@ UnpackedPath search(SearchEngineData<Algorithm> &engine_working_data,
|
|||||||
EdgeWeight weight_upper_bound,
|
EdgeWeight weight_upper_bound,
|
||||||
Args... args)
|
Args... args)
|
||||||
{
|
{
|
||||||
if (forward_heap.Empty() && reverse_heap.Empty())
|
if (forward_heap.Empty() || reverse_heap.Empty())
|
||||||
{
|
{
|
||||||
return std::make_tuple(INVALID_EDGE_WEIGHT, std::vector<NodeID>(), std::vector<EdgeID>());
|
return std::make_tuple(INVALID_EDGE_WEIGHT, std::vector<NodeID>(), std::vector<EdgeID>());
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &partition = facade.GetMultiLevelPartition();
|
const auto &partition = facade.GetMultiLevelPartition();
|
||||||
|
|
||||||
BOOST_ASSERT(forward_heap.Empty() || forward_heap.MinKey() < INVALID_EDGE_WEIGHT);
|
BOOST_ASSERT(!forward_heap.Empty() && forward_heap.MinKey() < INVALID_EDGE_WEIGHT);
|
||||||
BOOST_ASSERT(reverse_heap.Empty() || reverse_heap.MinKey() < INVALID_EDGE_WEIGHT);
|
BOOST_ASSERT(!reverse_heap.Empty() && reverse_heap.MinKey() < INVALID_EDGE_WEIGHT);
|
||||||
|
|
||||||
// run two-Target Dijkstra routing step.
|
// run two-Target Dijkstra routing step.
|
||||||
NodeID middle = SPECIAL_NODEID;
|
NodeID middle = SPECIAL_NODEID;
|
||||||
EdgeWeight weight = weight_upper_bound;
|
EdgeWeight weight = weight_upper_bound;
|
||||||
|
EdgeWeight forward_heap_min = forward_heap.MinKey();
|
||||||
EdgeWeight forward_heap_min = 0;
|
EdgeWeight reverse_heap_min = reverse_heap.MinKey();
|
||||||
if (!forward_heap.Empty())
|
|
||||||
forward_heap_min = forward_heap.MinKey();
|
|
||||||
EdgeWeight reverse_heap_min = 0;
|
|
||||||
if (!reverse_heap.Empty())
|
|
||||||
reverse_heap_min = reverse_heap.MinKey();
|
|
||||||
|
|
||||||
while (forward_heap.Size() + reverse_heap.Size() > 0 &&
|
while (forward_heap.Size() + reverse_heap.Size() > 0 &&
|
||||||
forward_heap_min + reverse_heap_min < weight)
|
forward_heap_min + reverse_heap_min < weight)
|
||||||
{
|
{
|
||||||
@@ -662,7 +657,11 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
|||||||
const PhantomNode &target_phantom,
|
const PhantomNode &target_phantom,
|
||||||
EdgeWeight weight_upper_bound = INVALID_EDGE_WEIGHT)
|
EdgeWeight weight_upper_bound = INVALID_EDGE_WEIGHT)
|
||||||
{
|
{
|
||||||
|
forward_heap.Clear();
|
||||||
|
reverse_heap.Clear();
|
||||||
|
|
||||||
const PhantomNodes phantom_nodes{source_phantom, target_phantom};
|
const PhantomNodes phantom_nodes{source_phantom, target_phantom};
|
||||||
|
insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes);
|
||||||
|
|
||||||
EdgeWeight weight = INVALID_EDGE_WEIGHT;
|
EdgeWeight weight = INVALID_EDGE_WEIGHT;
|
||||||
std::vector<NodeID> unpacked_nodes;
|
std::vector<NodeID> unpacked_nodes;
|
||||||
@@ -681,22 +680,11 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
|||||||
return std::numeric_limits<double>::max();
|
return std::numeric_limits<double>::max();
|
||||||
}
|
}
|
||||||
|
|
||||||
EdgeDistance distance = 0;
|
std::vector<PathData> unpacked_path;
|
||||||
|
|
||||||
if (!unpacked_nodes.empty())
|
annotatePath(facade, phantom_nodes, unpacked_nodes, unpacked_edges, unpacked_path);
|
||||||
{
|
|
||||||
distance = std::accumulate(unpacked_nodes.begin(),
|
|
||||||
std::prev(unpacked_nodes.end()),
|
|
||||||
EdgeDistance{0},
|
|
||||||
[&](const EdgeDistance distance, const auto node_id) {
|
|
||||||
return distance + computeEdgeDistance(facade, node_id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
distance = adjustPathDistanceToPhantomNodes(
|
return getPathDistance(facade, unpacked_path, source_phantom, target_phantom);
|
||||||
unpacked_nodes, phantom_nodes.source_phantom, phantom_nodes.target_phantom, distance);
|
|
||||||
|
|
||||||
return distance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mld
|
} // namespace mld
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define OSRM_BINDINGS_NODE_SUPPORT_HPP
|
#define OSRM_BINDINGS_NODE_SUPPORT_HPP
|
||||||
|
|
||||||
#include "nodejs/json_v8_renderer.hpp"
|
#include "nodejs/json_v8_renderer.hpp"
|
||||||
|
#include "util/json_renderer.hpp"
|
||||||
|
|
||||||
#include "osrm/approach.hpp"
|
#include "osrm/approach.hpp"
|
||||||
#include "osrm/bearing.hpp"
|
#include "osrm/bearing.hpp"
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -42,6 +44,13 @@ using match_parameters_ptr = std::unique_ptr<osrm::MatchParameters>;
|
|||||||
using nearest_parameters_ptr = std::unique_ptr<osrm::NearestParameters>;
|
using nearest_parameters_ptr = std::unique_ptr<osrm::NearestParameters>;
|
||||||
using table_parameters_ptr = std::unique_ptr<osrm::TableParameters>;
|
using table_parameters_ptr = std::unique_ptr<osrm::TableParameters>;
|
||||||
|
|
||||||
|
struct PluginParameters
|
||||||
|
{
|
||||||
|
bool renderJSONToBuffer = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ObjectOrString = typename mapbox::util::variant<osrm::json::Object, std::string>;
|
||||||
|
|
||||||
template <typename ResultT> inline v8::Local<v8::Value> render(const ResultT &result);
|
template <typename ResultT> inline v8::Local<v8::Value> render(const ResultT &result);
|
||||||
|
|
||||||
template <> v8::Local<v8::Value> inline render(const std::string &result)
|
template <> v8::Local<v8::Value> inline render(const std::string &result)
|
||||||
@@ -49,11 +58,21 @@ template <> v8::Local<v8::Value> inline render(const std::string &result)
|
|||||||
return Nan::CopyBuffer(result.data(), result.size()).ToLocalChecked();
|
return Nan::CopyBuffer(result.data(), result.size()).ToLocalChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> v8::Local<v8::Value> inline render(const osrm::json::Object &result)
|
template <> v8::Local<v8::Value> inline render(const ObjectOrString &result)
|
||||||
{
|
{
|
||||||
v8::Local<v8::Value> value;
|
if (result.is<osrm::json::Object>())
|
||||||
renderToV8(value, result);
|
{
|
||||||
return value;
|
// Convert osrm::json object tree into matching v8 object tree
|
||||||
|
v8::Local<v8::Value> value;
|
||||||
|
renderToV8(value, result.get<osrm::json::Object>());
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Return the string object as a node Buffer
|
||||||
|
return Nan::CopyBuffer(result.get<std::string>().data(), result.get<std::string>().size())
|
||||||
|
.ToLocalChecked();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void ParseResult(const osrm::Status &result_status, osrm::json::Object &result)
|
inline void ParseResult(const osrm::Status &result_status, osrm::json::Object &result)
|
||||||
@@ -814,6 +833,50 @@ inline bool parseCommonParameters(const v8::Local<v8::Object> &obj, ParamType &p
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline PluginParameters
|
||||||
|
argumentsToPluginParameters(const Nan::FunctionCallbackInfo<v8::Value> &args)
|
||||||
|
{
|
||||||
|
if (args.Length() < 3 || !args[1]->IsObject())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
v8::Local<v8::Object> obj = Nan::To<v8::Object>(args[1]).ToLocalChecked();
|
||||||
|
if (obj->Has(Nan::New("format").ToLocalChecked()))
|
||||||
|
{
|
||||||
|
|
||||||
|
v8::Local<v8::Value> format = obj->Get(Nan::New("format").ToLocalChecked());
|
||||||
|
if (format.IsEmpty())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!format->IsString())
|
||||||
|
{
|
||||||
|
Nan::ThrowError("format must be a string: \"object\" or \"json_buffer\"");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const Nan::Utf8String format_utf8str(format);
|
||||||
|
std::string format_str{*format_utf8str, *format_utf8str + format_utf8str.length()};
|
||||||
|
|
||||||
|
if (format_str == "object")
|
||||||
|
{
|
||||||
|
return {false};
|
||||||
|
}
|
||||||
|
else if (format_str == "json_buffer")
|
||||||
|
{
|
||||||
|
return {true};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Nan::ThrowError("format must be a string: \"object\" or \"json_buffer\"");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
inline route_parameters_ptr
|
inline route_parameters_ptr
|
||||||
argumentsToRouteParameter(const Nan::FunctionCallbackInfo<v8::Value> &args,
|
argumentsToRouteParameter(const Nan::FunctionCallbackInfo<v8::Value> &args,
|
||||||
bool requires_multiple_coordinates)
|
bool requires_multiple_coordinates)
|
||||||
@@ -1357,6 +1420,6 @@ argumentsToMatchParameter(const Nan::FunctionCallbackInfo<v8::Value> &args,
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // ns node_osrm
|
} // namespace node_osrm
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ namespace detail
|
|||||||
{
|
{
|
||||||
const constexpr double DEGREE_TO_RAD = 0.017453292519943295769236907684886;
|
const constexpr double DEGREE_TO_RAD = 0.017453292519943295769236907684886;
|
||||||
const constexpr double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD;
|
const constexpr double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD;
|
||||||
|
// earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi)
|
||||||
|
// The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles)
|
||||||
|
const constexpr long double EARTH_RADIUS = 6372797.560856;
|
||||||
|
|
||||||
inline double degToRad(const double degree)
|
inline double degToRad(const double degree)
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "osrm",
|
"name": "osrm",
|
||||||
"version": "5.18.0-latest.1",
|
"version": "5.19.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.",
|
"description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -240,12 +240,74 @@ void calculateDistances(typename SearchEngineData<ch::Algorithm>::ManyToManyQuer
|
|||||||
}
|
}
|
||||||
if (!packed_leg.empty())
|
if (!packed_leg.empty())
|
||||||
{
|
{
|
||||||
EdgeDistance annotation =
|
auto annotation =
|
||||||
ch::calculateEBGNodeAnnotations(facade, packed_leg.begin(), packed_leg.end());
|
ch::calculateEBGNodeAnnotations(facade, packed_leg.begin(), packed_leg.end());
|
||||||
annotation = adjustPathDistanceToPhantomNodes(
|
|
||||||
packed_leg, source_phantom, target_phantom, annotation);
|
|
||||||
|
|
||||||
distances_table[row_index * number_of_targets + column_index] = annotation;
|
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();
|
packed_leg.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,9 +227,6 @@ SubMatchingList mapMatching(SearchEngineData<Algorithm> &engine_working_data,
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
forward_heap.Clear();
|
|
||||||
const auto &source_phantom = prev_unbroken_timestamps_list[s].phantom_node;
|
|
||||||
insertSourceInHeap(forward_heap, source_phantom);
|
|
||||||
|
|
||||||
for (const auto s_prime : util::irange<std::size_t>(0UL, current_viterbi.size()))
|
for (const auto s_prime : util::irange<std::size_t>(0UL, current_viterbi.size()))
|
||||||
{
|
{
|
||||||
@@ -240,19 +237,14 @@ SubMatchingList mapMatching(SearchEngineData<Algorithm> &engine_working_data,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
reverse_heap.Clear();
|
double network_distance =
|
||||||
const auto &target_phantom = current_timestamps_list[s_prime].phantom_node;
|
getNetworkDistance(engine_working_data,
|
||||||
insertTargetInHeap(reverse_heap, target_phantom);
|
facade,
|
||||||
|
forward_heap,
|
||||||
double network_distance = getNetworkDistance(engine_working_data,
|
reverse_heap,
|
||||||
facade,
|
prev_unbroken_timestamps_list[s].phantom_node,
|
||||||
forward_heap,
|
current_timestamps_list[s_prime].phantom_node,
|
||||||
reverse_heap,
|
weight_upper_bound);
|
||||||
source_phantom,
|
|
||||||
target_phantom,
|
|
||||||
weight_upper_bound);
|
|
||||||
|
|
||||||
network_distance = std::round(network_distance * 10) / 10;
|
|
||||||
|
|
||||||
// get distance diff between loc1/2 and locs/s_prime
|
// get distance diff between loc1/2 and locs/s_prime
|
||||||
const auto d_t = std::abs(network_distance - haversine_distance);
|
const auto d_t = std::abs(network_distance - haversine_distance);
|
||||||
|
|||||||
@@ -33,79 +33,6 @@ bool needsLoopBackwards(const PhantomNodes &phantoms)
|
|||||||
return needsLoopBackwards(phantoms.source_phantom, phantoms.target_phantom);
|
return needsLoopBackwards(phantoms.source_phantom, phantoms.target_phantom);
|
||||||
}
|
}
|
||||||
|
|
||||||
EdgeDistance adjustPathDistanceToPhantomNodes(const std::vector<NodeID> &path,
|
|
||||||
const PhantomNode &source_phantom,
|
|
||||||
const PhantomNode &target_phantom,
|
|
||||||
const EdgeDistance uncorrected_distance)
|
|
||||||
{
|
|
||||||
EdgeDistance distance = uncorrected_distance;
|
|
||||||
if (!path.empty())
|
|
||||||
{
|
|
||||||
|
|
||||||
// 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 == path.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
|
|
||||||
distance -= source_phantom.GetForwardDistance();
|
|
||||||
}
|
|
||||||
else if (source_phantom.reverse_segment_id.id == path.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
|
|
||||||
distance -= source_phantom.GetReverseDistance();
|
|
||||||
}
|
|
||||||
if (target_phantom.forward_segment_id.id == path.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
|
|
||||||
distance += target_phantom.GetForwardDistance();
|
|
||||||
}
|
|
||||||
else if (target_phantom.reverse_segment_id.id == path.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
|
|
||||||
distance += target_phantom.GetReverseDistance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
distance = target_phantom.GetForwardDistance() - source_phantom.GetForwardDistance();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// s<--- <-- offsets
|
|
||||||
// t<--------- <-- subtract source offset from target offset
|
|
||||||
// ...... <-- want this distance as result
|
|
||||||
// entry 0---1---2---3--- <-- 3 is exit node
|
|
||||||
distance = target_phantom.GetReverseDistance() - source_phantom.GetReverseDistance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_ASSERT_MSG(distance >= 0 || distance > -1.0f,
|
|
||||||
"Distance correction generated negative number");
|
|
||||||
// guard against underflow errors caused by rounding
|
|
||||||
distance = std::max(EdgeDistance{0}, distance);
|
|
||||||
return distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace routing_algorithms
|
} // namespace routing_algorithms
|
||||||
} // namespace engine
|
} // namespace engine
|
||||||
} // namespace osrm
|
} // namespace osrm
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ void search(SearchEngineData<Algorithm> & /*engine_working_data*/,
|
|||||||
const PhantomNodes & /*phantom_nodes*/,
|
const PhantomNodes & /*phantom_nodes*/,
|
||||||
const EdgeWeight weight_upper_bound)
|
const EdgeWeight weight_upper_bound)
|
||||||
{
|
{
|
||||||
if (forward_heap.Empty() && reverse_heap.Empty())
|
if (forward_heap.Empty() || reverse_heap.Empty())
|
||||||
{
|
{
|
||||||
weight = INVALID_EDGE_WEIGHT;
|
weight = INVALID_EDGE_WEIGHT;
|
||||||
return;
|
return;
|
||||||
@@ -110,14 +110,10 @@ void search(SearchEngineData<Algorithm> & /*engine_working_data*/,
|
|||||||
weight = weight_upper_bound;
|
weight = weight_upper_bound;
|
||||||
|
|
||||||
// get offset to account for offsets on phantom nodes on compressed edges
|
// get offset to account for offsets on phantom nodes on compressed edges
|
||||||
EdgeWeight min_edge_offset = 0;
|
const auto min_edge_offset = std::min(0, forward_heap.MinKey());
|
||||||
if (forward_heap.Size() > 0)
|
BOOST_ASSERT(min_edge_offset <= 0);
|
||||||
{
|
|
||||||
min_edge_offset = std::min(min_edge_offset, forward_heap.MinKey());
|
|
||||||
BOOST_ASSERT(min_edge_offset <= 0);
|
|
||||||
}
|
|
||||||
// we only every insert negative offsets for nodes in the forward heap
|
// we only every insert negative offsets for nodes in the forward heap
|
||||||
BOOST_ASSERT(reverse_heap.Empty() || reverse_heap.MinKey() >= 0);
|
BOOST_ASSERT(reverse_heap.MinKey() >= 0);
|
||||||
|
|
||||||
// run two-Target Dijkstra routing step.
|
// run two-Target Dijkstra routing step.
|
||||||
while (0 < (forward_heap.Size() + reverse_heap.Size()))
|
while (0 < (forward_heap.Size() + reverse_heap.Size()))
|
||||||
@@ -180,6 +176,11 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
|||||||
const PhantomNode &target_phantom,
|
const PhantomNode &target_phantom,
|
||||||
EdgeWeight weight_upper_bound)
|
EdgeWeight weight_upper_bound)
|
||||||
{
|
{
|
||||||
|
forward_heap.Clear();
|
||||||
|
reverse_heap.Clear();
|
||||||
|
|
||||||
|
insertNodesInHeaps(forward_heap, reverse_heap, {source_phantom, target_phantom});
|
||||||
|
|
||||||
EdgeWeight weight = INVALID_EDGE_WEIGHT;
|
EdgeWeight weight = INVALID_EDGE_WEIGHT;
|
||||||
std::vector<NodeID> packed_path;
|
std::vector<NodeID> packed_path;
|
||||||
search(engine_working_data,
|
search(engine_working_data,
|
||||||
@@ -198,31 +199,14 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
|||||||
return std::numeric_limits<double>::max();
|
return std::numeric_limits<double>::max();
|
||||||
}
|
}
|
||||||
|
|
||||||
EdgeDistance distance = 0;
|
std::vector<PathData> unpacked_path;
|
||||||
|
unpackPath(facade,
|
||||||
|
packed_path.begin(),
|
||||||
|
packed_path.end(),
|
||||||
|
{source_phantom, target_phantom},
|
||||||
|
unpacked_path);
|
||||||
|
|
||||||
std::vector<NodeID> unpacked_nodes;
|
return getPathDistance(facade, unpacked_path, source_phantom, target_phantom);
|
||||||
unpacked_nodes.reserve(packed_path.size());
|
|
||||||
if (!packed_path.empty())
|
|
||||||
{
|
|
||||||
unpacked_nodes.push_back(packed_path.front());
|
|
||||||
unpackPath(
|
|
||||||
facade, packed_path.begin(), packed_path.end(), [&](const auto &edge, const auto &) {
|
|
||||||
BOOST_ASSERT(edge.first == unpacked_nodes.back());
|
|
||||||
unpacked_nodes.push_back(edge.second);
|
|
||||||
});
|
|
||||||
|
|
||||||
distance = std::accumulate(unpacked_nodes.begin(),
|
|
||||||
std::prev(unpacked_nodes.end()),
|
|
||||||
EdgeDistance{0},
|
|
||||||
[&](const EdgeDistance distance, const auto node_id) {
|
|
||||||
return distance + computeEdgeDistance(facade, node_id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
distance =
|
|
||||||
adjustPathDistanceToPhantomNodes(unpacked_nodes, source_phantom, target_phantom, distance);
|
|
||||||
|
|
||||||
return distance;
|
|
||||||
}
|
}
|
||||||
} // namespace ch
|
} // namespace ch
|
||||||
|
|
||||||
|
|||||||
+92
-11
@@ -9,12 +9,15 @@
|
|||||||
#include "osrm/trip_parameters.hpp"
|
#include "osrm/trip_parameters.hpp"
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <sstream>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "nodejs/node_osrm.hpp"
|
#include "nodejs/node_osrm.hpp"
|
||||||
#include "nodejs/node_osrm_support.hpp"
|
#include "nodejs/node_osrm_support.hpp"
|
||||||
|
|
||||||
|
#include "util/json_renderer.hpp"
|
||||||
|
|
||||||
namespace node_osrm
|
namespace node_osrm
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -122,6 +125,8 @@ inline void async(const Nan::FunctionCallbackInfo<v8::Value> &info,
|
|||||||
if (!params)
|
if (!params)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
auto pluginParams = argumentsToPluginParameters(info);
|
||||||
|
|
||||||
BOOST_ASSERT(params->IsValid());
|
BOOST_ASSERT(params->IsValid());
|
||||||
|
|
||||||
if (!info[info.Length() - 1]->IsFunction())
|
if (!info[info.Length() - 1]->IsFunction())
|
||||||
@@ -137,9 +142,89 @@ inline void async(const Nan::FunctionCallbackInfo<v8::Value> &info,
|
|||||||
Worker(std::shared_ptr<osrm::OSRM> osrm_,
|
Worker(std::shared_ptr<osrm::OSRM> osrm_,
|
||||||
ParamPtr params_,
|
ParamPtr params_,
|
||||||
ServiceMemFn service,
|
ServiceMemFn service,
|
||||||
Nan::Callback *callback)
|
Nan::Callback *callback,
|
||||||
|
PluginParameters pluginParams_)
|
||||||
: Base(callback), osrm{std::move(osrm_)}, service{std::move(service)},
|
: Base(callback), osrm{std::move(osrm_)}, service{std::move(service)},
|
||||||
params{std::move(params_)}
|
params{std::move(params_)}, pluginParams{std::move(pluginParams_)}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Execute() override try
|
||||||
|
{
|
||||||
|
osrm::json::Object r;
|
||||||
|
const auto status = ((*osrm).*(service))(*params, r);
|
||||||
|
ParseResult(status, r);
|
||||||
|
if (pluginParams.renderJSONToBuffer)
|
||||||
|
{
|
||||||
|
std::ostringstream buf;
|
||||||
|
osrm::util::json::render(buf, r);
|
||||||
|
result = buf.str();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
SetErrorMessage(e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleOKCallback() override
|
||||||
|
{
|
||||||
|
Nan::HandleScope scope;
|
||||||
|
|
||||||
|
const constexpr auto argc = 2u;
|
||||||
|
v8::Local<v8::Value> argv[argc] = {Nan::Null(), render(result)};
|
||||||
|
|
||||||
|
callback->Call(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keeps the OSRM object alive even after shutdown until we're done with callback
|
||||||
|
std::shared_ptr<osrm::OSRM> osrm;
|
||||||
|
ServiceMemFn service;
|
||||||
|
const ParamPtr params;
|
||||||
|
const PluginParameters pluginParams;
|
||||||
|
|
||||||
|
ObjectOrString result;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto *callback = new Nan::Callback{info[info.Length() - 1].As<v8::Function>()};
|
||||||
|
Nan::AsyncQueueWorker(
|
||||||
|
new Worker{self->this_, std::move(params), service, callback, std::move(pluginParams)});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ParameterParser, typename ServiceMemFn>
|
||||||
|
inline void asyncForTiles(const Nan::FunctionCallbackInfo<v8::Value> &info,
|
||||||
|
ParameterParser argsToParams,
|
||||||
|
ServiceMemFn service,
|
||||||
|
bool requires_multiple_coordinates)
|
||||||
|
{
|
||||||
|
auto params = argsToParams(info, requires_multiple_coordinates);
|
||||||
|
if (!params)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto pluginParams = argumentsToPluginParameters(info);
|
||||||
|
|
||||||
|
BOOST_ASSERT(params->IsValid());
|
||||||
|
|
||||||
|
if (!info[info.Length() - 1]->IsFunction())
|
||||||
|
return Nan::ThrowTypeError("last argument must be a callback function");
|
||||||
|
|
||||||
|
auto *const self = Nan::ObjectWrap::Unwrap<Engine>(info.Holder());
|
||||||
|
using ParamPtr = decltype(params);
|
||||||
|
|
||||||
|
struct Worker final : Nan::AsyncWorker
|
||||||
|
{
|
||||||
|
using Base = Nan::AsyncWorker;
|
||||||
|
|
||||||
|
Worker(std::shared_ptr<osrm::OSRM> osrm_,
|
||||||
|
ParamPtr params_,
|
||||||
|
ServiceMemFn service,
|
||||||
|
Nan::Callback *callback,
|
||||||
|
PluginParameters pluginParams_)
|
||||||
|
: Base(callback), osrm{std::move(osrm_)}, service{std::move(service)},
|
||||||
|
params{std::move(params_)}, pluginParams{std::move(pluginParams_)}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,18 +252,14 @@ inline void async(const Nan::FunctionCallbackInfo<v8::Value> &info,
|
|||||||
std::shared_ptr<osrm::OSRM> osrm;
|
std::shared_ptr<osrm::OSRM> osrm;
|
||||||
ServiceMemFn service;
|
ServiceMemFn service;
|
||||||
const ParamPtr params;
|
const ParamPtr params;
|
||||||
|
const PluginParameters pluginParams;
|
||||||
|
|
||||||
// All services return json::Object .. except for Tile!
|
std::string result;
|
||||||
using ObjectOrString =
|
|
||||||
typename std::conditional<std::is_same<ParamPtr, tile_parameters_ptr>::value,
|
|
||||||
std::string,
|
|
||||||
osrm::json::Object>::type;
|
|
||||||
|
|
||||||
ObjectOrString result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auto *callback = new Nan::Callback{info[info.Length() - 1].As<v8::Function>()};
|
auto *callback = new Nan::Callback{info[info.Length() - 1].As<v8::Function>()};
|
||||||
Nan::AsyncQueueWorker(new Worker{self->this_, std::move(params), service, callback});
|
Nan::AsyncQueueWorker(
|
||||||
|
new Worker{self->this_, std::move(params), service, callback, std::move(pluginParams)});
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@@ -341,7 +422,7 @@ NAN_METHOD(Engine::table) //
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
NAN_METHOD(Engine::tile)
|
NAN_METHOD(Engine::tile)
|
||||||
{
|
{
|
||||||
async(info, &argumentsToTileParameters, &osrm::OSRM::Tile, {/*unused*/});
|
asyncForTiles(info, &argumentsToTileParameters, &osrm::OSRM::Tile, {/*unused*/});
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|||||||
+9
-13
@@ -70,8 +70,8 @@ std::istream &operator>>(std::istream &in, EngineConfig::Algorithm &algorithm)
|
|||||||
throw util::RuntimeError(token, ErrorCode::UnknownAlgorithm, SOURCE_REF);
|
throw util::RuntimeError(token, ErrorCode::UnknownAlgorithm, SOURCE_REF);
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
}
|
} // namespace engine
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
// generate boost::program_options object for the routing part
|
// generate boost::program_options object for the routing part
|
||||||
inline unsigned generateServerProgramOptions(const int argc,
|
inline unsigned generateServerProgramOptions(const int argc,
|
||||||
@@ -273,10 +273,12 @@ int main(int argc, const char *argv[]) try
|
|||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
int sig = 0;
|
int sig = 0;
|
||||||
sigset_t new_mask;
|
sigset_t wait_mask;
|
||||||
sigset_t old_mask;
|
sigemptyset(&wait_mask);
|
||||||
sigfillset(&new_mask);
|
sigaddset(&wait_mask, SIGINT);
|
||||||
pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask);
|
sigaddset(&wait_mask, SIGQUIT);
|
||||||
|
sigaddset(&wait_mask, SIGTERM);
|
||||||
|
pthread_sigmask(SIG_BLOCK, &wait_mask, nullptr); // only block necessary signals
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto service_handler = std::make_unique<server::ServiceHandler>(config);
|
auto service_handler = std::make_unique<server::ServiceHandler>(config);
|
||||||
@@ -298,19 +300,13 @@ int main(int argc, const char *argv[]) try
|
|||||||
std::thread server_thread(std::move(server_task));
|
std::thread server_thread(std::move(server_task));
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
sigset_t wait_mask;
|
|
||||||
pthread_sigmask(SIG_SETMASK, &old_mask, nullptr);
|
|
||||||
sigemptyset(&wait_mask);
|
|
||||||
sigaddset(&wait_mask, SIGINT);
|
|
||||||
sigaddset(&wait_mask, SIGQUIT);
|
|
||||||
sigaddset(&wait_mask, SIGTERM);
|
|
||||||
pthread_sigmask(SIG_BLOCK, &wait_mask, nullptr);
|
|
||||||
util::Log() << "running and waiting for requests";
|
util::Log() << "running and waiting for requests";
|
||||||
if (std::getenv("SIGNAL_PARENT_WHEN_READY"))
|
if (std::getenv("SIGNAL_PARENT_WHEN_READY"))
|
||||||
{
|
{
|
||||||
kill(getppid(), SIGUSR1);
|
kill(getppid(), SIGUSR1);
|
||||||
}
|
}
|
||||||
sigwait(&wait_mask, &sig);
|
sigwait(&wait_mask, &sig);
|
||||||
|
util::Log() << "received signal " << sig;
|
||||||
#else
|
#else
|
||||||
// Set console control handler to allow server to be stopped.
|
// Set console control handler to allow server to be stopped.
|
||||||
console_ctrl_function = std::bind(&server::Server::Stop, routing_server);
|
console_ctrl_function = std::bind(&server::Server::Stop, routing_server);
|
||||||
|
|||||||
@@ -22,11 +22,6 @@ namespace coordinate_calculation
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
// earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi)
|
|
||||||
// The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles)
|
|
||||||
const constexpr double EARTH_RADIUS = 6372797.560856;
|
|
||||||
|
|
||||||
class CheapRulerContainer
|
class CheapRulerContainer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -117,7 +112,7 @@ double haversineDistance(const Coordinate coordinate_1, const Coordinate coordin
|
|||||||
const double aharv = std::pow(std::sin(dlat / 2.0), 2.0) +
|
const double aharv = std::pow(std::sin(dlat / 2.0), 2.0) +
|
||||||
std::cos(dlat1) * std::cos(dlat2) * std::pow(std::sin(dlong / 2.), 2);
|
std::cos(dlat1) * std::cos(dlat2) * std::pow(std::sin(dlong / 2.), 2);
|
||||||
const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv));
|
const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv));
|
||||||
return EARTH_RADIUS * charv;
|
return detail::EARTH_RADIUS * charv;
|
||||||
}
|
}
|
||||||
|
|
||||||
double greatCircleDistance(const Coordinate coordinate_1, const Coordinate coordinate_2)
|
double greatCircleDistance(const Coordinate coordinate_1, const Coordinate coordinate_2)
|
||||||
@@ -138,7 +133,7 @@ double greatCircleDistance(const Coordinate coordinate_1, const Coordinate coord
|
|||||||
|
|
||||||
const double x_value = (float_lon2 - float_lon1) * std::cos((float_lat1 + float_lat2) / 2.0);
|
const double x_value = (float_lon2 - float_lon1) * std::cos((float_lat1 + float_lat2) / 2.0);
|
||||||
const double y_value = float_lat2 - float_lat1;
|
const double y_value = float_lat2 - float_lat1;
|
||||||
return std::hypot(x_value, y_value) * EARTH_RADIUS;
|
return std::hypot(x_value, y_value) * detail::EARTH_RADIUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
double perpendicularDistance(const Coordinate segment_source,
|
double perpendicularDistance(const Coordinate segment_source,
|
||||||
|
|||||||
@@ -25,6 +25,28 @@ test('match: match in Monaco', function(assert) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('match: match in Monaco returning a buffer', function(assert) {
|
||||||
|
assert.plan(6);
|
||||||
|
var osrm = new OSRM(data_path);
|
||||||
|
var options = {
|
||||||
|
coordinates: three_test_coordinates,
|
||||||
|
timestamps: [1424684612, 1424684616, 1424684620]
|
||||||
|
};
|
||||||
|
osrm.match(options, { format: 'json_buffer' }, function(err, response) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.ok(response instanceof Buffer);
|
||||||
|
response = JSON.parse(response);
|
||||||
|
assert.equal(response.matchings.length, 1);
|
||||||
|
assert.ok(response.matchings.every(function(m) {
|
||||||
|
return !!m.distance && !!m.duration && Array.isArray(m.legs) && !!m.geometry && m.confidence > 0;
|
||||||
|
}))
|
||||||
|
assert.equal(response.tracepoints.length, 3);
|
||||||
|
assert.ok(response.tracepoints.every(function(t) {
|
||||||
|
return !!t.hint && !isNaN(t.matchings_index) && !isNaN(t.waypoint_index) && !!t.name;
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('match: match in Monaco without timestamps', function(assert) {
|
test('match: match in Monaco without timestamps', function(assert) {
|
||||||
assert.plan(3);
|
assert.plan(3);
|
||||||
var osrm = new OSRM(data_path);
|
var osrm = new OSRM(data_path);
|
||||||
@@ -225,6 +247,16 @@ test('match: throws on invalid tidy param', function(assert) {
|
|||||||
/tidy must be of type Boolean/);
|
/tidy must be of type Boolean/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('match: throws on invalid config param', function(assert) {
|
||||||
|
assert.plan(1);
|
||||||
|
var osrm = new OSRM({path: mld_data_path, algorithm: 'MLD'});
|
||||||
|
var options = {
|
||||||
|
coordinates: three_test_coordinates,
|
||||||
|
};
|
||||||
|
assert.throws(function() { osrm.match(options, { format: 'invalid' }, function(err, response) {}) },
|
||||||
|
/format must be a string:/);
|
||||||
|
});
|
||||||
|
|
||||||
test('match: match in Monaco without motorways', function(assert) {
|
test('match: match in Monaco without motorways', function(assert) {
|
||||||
assert.plan(3);
|
assert.plan(3);
|
||||||
var osrm = new OSRM({path: mld_data_path, algorithm: 'MLD'});
|
var osrm = new OSRM({path: mld_data_path, algorithm: 'MLD'});
|
||||||
|
|||||||
+20
-1
@@ -19,6 +19,21 @@ test('nearest', function(assert) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('nearest', function(assert) {
|
||||||
|
assert.plan(5);
|
||||||
|
var osrm = new OSRM(data_path);
|
||||||
|
osrm.nearest({
|
||||||
|
coordinates: [three_test_coordinates[0]]
|
||||||
|
}, { format: 'json_buffer' }, function(err, result) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.ok(result instanceof Buffer);
|
||||||
|
result = JSON.parse(result);
|
||||||
|
assert.equal(result.waypoints.length, 1);
|
||||||
|
assert.equal(result.waypoints[0].location.length, 2);
|
||||||
|
assert.ok(result.waypoints[0].hasOwnProperty('name'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('nearest: can ask for multiple nearest pts', function(assert) {
|
test('nearest: can ask for multiple nearest pts', function(assert) {
|
||||||
assert.plan(2);
|
assert.plan(2);
|
||||||
var osrm = new OSRM(data_path);
|
var osrm = new OSRM(data_path);
|
||||||
@@ -32,7 +47,7 @@ test('nearest: can ask for multiple nearest pts', function(assert) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('nearest: throws on invalid args', function(assert) {
|
test('nearest: throws on invalid args', function(assert) {
|
||||||
assert.plan(6);
|
assert.plan(7);
|
||||||
var osrm = new OSRM(data_path);
|
var osrm = new OSRM(data_path);
|
||||||
var options = {};
|
var options = {};
|
||||||
assert.throws(function() { osrm.nearest(options); },
|
assert.throws(function() { osrm.nearest(options); },
|
||||||
@@ -52,6 +67,10 @@ test('nearest: throws on invalid args', function(assert) {
|
|||||||
options.number = 0;
|
options.number = 0;
|
||||||
assert.throws(function() { osrm.nearest(options, function(err, res) {}); },
|
assert.throws(function() { osrm.nearest(options, function(err, res) {}); },
|
||||||
/Number must be an integer greater than or equal to 1/);
|
/Number must be an integer greater than or equal to 1/);
|
||||||
|
|
||||||
|
options.number = 1;
|
||||||
|
assert.throws(function() { osrm.nearest(options, { format: 'invalid' }, function(err, res) {}); },
|
||||||
|
/format must be a string:/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('nearest: nearest in Monaco without motorways', function(assert) {
|
test('nearest: nearest in Monaco without motorways', function(assert) {
|
||||||
|
|||||||
+17
-1
@@ -43,8 +43,22 @@ test('route: routes Monaco on CoreCH', function(assert) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('route: routes Monaco and returns a JSON buffer', function(assert) {
|
||||||
|
assert.plan(6);
|
||||||
|
var osrm = new OSRM({path: monaco_corech_path, algorithm: 'CoreCH'});
|
||||||
|
osrm.route({coordinates: [[13.43864,52.51993],[13.415852,52.513191]]}, { format: 'json_buffer'}, function(err, result) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.ok(result instanceof Buffer);
|
||||||
|
const route = JSON.parse(result);
|
||||||
|
assert.ok(route.waypoints);
|
||||||
|
assert.ok(route.routes);
|
||||||
|
assert.ok(route.routes.length);
|
||||||
|
assert.ok(route.routes[0].geometry);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('route: throws with too few or invalid args', function(assert) {
|
test('route: throws with too few or invalid args', function(assert) {
|
||||||
assert.plan(3);
|
assert.plan(4);
|
||||||
var osrm = new OSRM(monaco_path);
|
var osrm = new OSRM(monaco_path);
|
||||||
assert.throws(function() { osrm.route({coordinates: two_test_coordinates}) },
|
assert.throws(function() { osrm.route({coordinates: two_test_coordinates}) },
|
||||||
/Two arguments required/);
|
/Two arguments required/);
|
||||||
@@ -52,6 +66,8 @@ test('route: throws with too few or invalid args', function(assert) {
|
|||||||
/First arg must be an object/);
|
/First arg must be an object/);
|
||||||
assert.throws(function() { osrm.route({coordinates: two_test_coordinates}, true)},
|
assert.throws(function() { osrm.route({coordinates: two_test_coordinates}, true)},
|
||||||
/last argument must be a callback function/);
|
/last argument must be a callback function/);
|
||||||
|
assert.throws(function() { osrm.route({coordinates: two_test_coordinates}, { format: 'invalid' }, function(err, route) {})},
|
||||||
|
/format must be a string:/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('route: provides no alternatives by default, but when requested it may (not guaranteed)', function(assert) {
|
test('route: provides no alternatives by default, but when requested it may (not guaranteed)', function(assert) {
|
||||||
|
|||||||
+18
-1
@@ -48,6 +48,20 @@ test('table: test annotations paramater combination', function(assert) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('table: returns buffer', function(assert) {
|
||||||
|
assert.plan(3);
|
||||||
|
var osrm = new OSRM(data_path);
|
||||||
|
var options = {
|
||||||
|
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
|
||||||
|
};
|
||||||
|
osrm.table(options, { format: 'json_buffer' }, function(err, table) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.ok(table instanceof Buffer);
|
||||||
|
table = JSON.parse(table);
|
||||||
|
assert.ok(table['durations'], 'distances table result should exist');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
var tables = ['distances', 'durations'];
|
var tables = ['distances', 'durations'];
|
||||||
|
|
||||||
tables.forEach(function(annotation) {
|
tables.forEach(function(annotation) {
|
||||||
@@ -116,7 +130,7 @@ tables.forEach(function(annotation) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('table: ' + annotation + ' throws on invalid arguments', function(assert) {
|
test('table: ' + annotation + ' throws on invalid arguments', function(assert) {
|
||||||
assert.plan(14);
|
assert.plan(15);
|
||||||
var osrm = new OSRM(data_path);
|
var osrm = new OSRM(data_path);
|
||||||
var options = {annotations: [annotation.slice(0,-1)]};
|
var options = {annotations: [annotation.slice(0,-1)]};
|
||||||
assert.throws(function() { osrm.table(options); },
|
assert.throws(function() { osrm.table(options); },
|
||||||
@@ -135,6 +149,9 @@ tables.forEach(function(annotation) {
|
|||||||
/Coordinates must be an array of \(lon\/lat\) pairs/);
|
/Coordinates must be an array of \(lon\/lat\) pairs/);
|
||||||
|
|
||||||
options.coordinates = two_test_coordinates;
|
options.coordinates = two_test_coordinates;
|
||||||
|
assert.throws(function() { osrm.table(options, { format: 'invalid' }, function(err, response) {}) },
|
||||||
|
/format must be a string:/);
|
||||||
|
|
||||||
options.sources = true;
|
options.sources = true;
|
||||||
assert.throws(function() { osrm.table(options, function(err, response) {}) },
|
assert.throws(function() { osrm.table(options, function(err, response) {}) },
|
||||||
/Sources must be an array of indices \(or undefined\)/);
|
/Sources must be an array of indices \(or undefined\)/);
|
||||||
|
|||||||
+16
-1
@@ -17,6 +17,19 @@ test('trip: trip in Monaco', function(assert) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('trip: trip in Monaco as a buffer', function(assert) {
|
||||||
|
assert.plan(3);
|
||||||
|
var osrm = new OSRM(data_path);
|
||||||
|
osrm.trip({coordinates: two_test_coordinates}, { format: 'json_buffer' }, function(err, trip) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.ok(trip instanceof Buffer);
|
||||||
|
trip = JSON.parse(trip);
|
||||||
|
for (t = 0; t < trip.trips.length; t++) {
|
||||||
|
assert.ok(trip.trips[t].geometry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('trip: trip with many locations in Monaco', function(assert) {
|
test('trip: trip with many locations in Monaco', function(assert) {
|
||||||
assert.plan(2);
|
assert.plan(2);
|
||||||
|
|
||||||
@@ -33,12 +46,14 @@ test('trip: trip with many locations in Monaco', function(assert) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('trip: throws with too few or invalid args', function(assert) {
|
test('trip: throws with too few or invalid args', function(assert) {
|
||||||
assert.plan(2);
|
assert.plan(3);
|
||||||
var osrm = new OSRM(data_path);
|
var osrm = new OSRM(data_path);
|
||||||
assert.throws(function() { osrm.trip({coordinates: two_test_coordinates}) },
|
assert.throws(function() { osrm.trip({coordinates: two_test_coordinates}) },
|
||||||
/Two arguments required/);
|
/Two arguments required/);
|
||||||
assert.throws(function() { osrm.trip(null, function(err, trip) {}) },
|
assert.throws(function() { osrm.trip(null, function(err, trip) {}) },
|
||||||
/First arg must be an object/);
|
/First arg must be an object/);
|
||||||
|
assert.throws(function() { osrm.trip({coordinates: two_test_coordinates}, { format: 'invalid' }, function(err, trip) {}) },
|
||||||
|
/format must be a string:/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('trip: throws with bad params', function(assert) {
|
test('trip: throws with bad params', function(assert) {
|
||||||
|
|||||||
Reference in New Issue
Block a user