~valhalla-routing/+junk/valhalla_2.1.9-0ubuntu1

« back to all changes in this revision

Viewing changes to libvalhalla/src/valhalla_run_route.cc

  • Committer: valhalla
  • Date: 2017-04-24 20:20:53 UTC
  • Revision ID: valhalla@mapzen.com-20170424202053-7o69b9nwx9ee0tw3
PackagingĀ forĀ 2.1.9-0ubuntu1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <iostream>
 
2
#include <string>
 
3
#include <vector>
 
4
#include <queue>
 
5
#include <tuple>
 
6
#include <cmath>
 
7
#include <boost/program_options.hpp>
 
8
#include <boost/property_tree/ptree.hpp>
 
9
#include <boost/property_tree/json_parser.hpp>
 
10
#include <boost/optional.hpp>
 
11
#include <boost/format.hpp>
 
12
 
 
13
#include "config.h"
 
14
 
 
15
#include "midgard/encoded.h"
 
16
#include "baldr/graphreader.h"
 
17
#include "baldr/tilehierarchy.h"
 
18
#include "baldr/pathlocation.h"
 
19
#include "baldr/connectivity_map.h"
 
20
#include "loki/search.h"
 
21
#include "sif/costfactory.h"
 
22
#include "odin/directionsbuilder.h"
 
23
#include "odin/util.h"
 
24
#include "proto/trippath.pb.h"
 
25
#include "proto/tripdirections.pb.h"
 
26
#include "proto/directions_options.pb.h"
 
27
#include "midgard/logging.h"
 
28
#include "midgard/distanceapproximator.h"
 
29
#include "thor/astar.h"
 
30
#include "thor/bidirectional_astar.h"
 
31
#include "thor/multimodal.h"
 
32
#include "thor/trippathbuilder.h"
 
33
#include "thor/attributes_controller.h"
 
34
#include "thor/route_matcher.h"
 
35
 
 
36
using namespace valhalla::midgard;
 
37
using namespace valhalla::baldr;
 
38
using namespace valhalla::loki;
 
39
using namespace valhalla::odin;
 
40
using namespace valhalla::sif;
 
41
using namespace valhalla::thor;
 
42
 
 
43
namespace bpo = boost::program_options;
 
44
 
 
45
namespace {
 
46
  class PathStatistics {
 
47
    std::pair<float, float> origin;
 
48
    std::pair<float, float> destination;
 
49
    std::string success;
 
50
    uint32_t passes;
 
51
    uint32_t runtime;
 
52
    uint32_t trip_time;
 
53
    float trip_dist;
 
54
    float arc_dist;
 
55
    uint32_t manuevers;
 
56
 
 
57
  public:
 
58
    PathStatistics (std::pair<float, float> p1, std::pair<float, float> p2)
 
59
      : origin(p1), destination(p2), success("false"),
 
60
        passes(0), runtime(), trip_time(),
 
61
        trip_dist(), arc_dist(), manuevers() { }
 
62
 
 
63
    void setSuccess(std::string s) { success = s; }
 
64
    void incPasses(void) { ++passes; }
 
65
    void addRuntime(uint32_t msec) { runtime += msec; }
 
66
    void setTripTime(uint32_t t) { trip_time = t; }
 
67
    void setTripDist(float d) { trip_dist = d; }
 
68
    void setArcDist(float d) { arc_dist = d; }
 
69
    void setManuevers(uint32_t n) { manuevers = n; }
 
70
    void log() {
 
71
      valhalla::midgard::logging::Log(
 
72
        (boost::format("%f,%f,%f,%f,%s,%d,%d,%d,%f,%f,%d")
 
73
          % origin.first % origin.second % destination.first % destination.second
 
74
          % success % passes % runtime % trip_time % trip_dist % arc_dist % manuevers).str(),
 
75
        " [STATISTICS] ");
 
76
    }
 
77
  };
 
78
}
 
79
 
 
80
/**
 
81
 * Test a single path from origin to destination.
 
82
 */
 
83
TripPath PathTest(GraphReader& reader, PathLocation& origin,
 
84
                  PathLocation& dest, PathAlgorithm* pathalgorithm,
 
85
                  const std::shared_ptr<DynamicCost>* mode_costing,
 
86
                  const TravelMode mode, PathStatistics& data,
 
87
                  bool multi_run, uint32_t iterations,
 
88
                  bool using_astar, bool match_test) {
 
89
  auto t1 = std::chrono::high_resolution_clock::now();
 
90
  std::vector<PathInfo> pathedges;
 
91
  pathedges = pathalgorithm->GetBestPath(origin, dest, reader, mode_costing, mode);
 
92
  cost_ptr_t cost = mode_costing[static_cast<uint32_t>(mode)];
 
93
  data.incPasses();
 
94
  if (pathedges.size() == 0) {
 
95
    if (cost->AllowMultiPass()) {
 
96
      LOG_INFO("Try again with relaxed hierarchy limits");
 
97
      pathalgorithm->Clear();
 
98
      float relax_factor = (using_astar) ? 16.0f : 8.0f;
 
99
      float expansion_within_factor = (using_astar) ? 4.0f : 2.0f;
 
100
      cost->RelaxHierarchyLimits(using_astar, expansion_within_factor);
 
101
      pathedges = pathalgorithm->GetBestPath(origin, dest, reader, mode_costing, mode);
 
102
      data.incPasses();
 
103
    }
 
104
  }
 
105
  if (pathedges.size() == 0) {
 
106
    // Return an empty trip path
 
107
    return TripPath();
 
108
  }
 
109
 
 
110
  auto t2 = std::chrono::high_resolution_clock::now();
 
111
  uint32_t msecs = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
 
112
  LOG_INFO("PathAlgorithm GetBestPath took " + std::to_string(msecs) + " ms");
 
113
 
 
114
  // Form trip path
 
115
  t1 = std::chrono::high_resolution_clock::now();
 
116
  AttributesController controller;
 
117
  TripPath trip_path = TripPathBuilder::Build(controller, reader, mode_costing,
 
118
                                              pathedges, origin, dest,
 
119
                                              std::list<PathLocation>{});
 
120
  t2 = std::chrono::high_resolution_clock::now();
 
121
  msecs = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
 
122
  LOG_INFO("TripPathBuilder took " + std::to_string(msecs) + " ms");
 
123
 
 
124
  // Time how long it takes to clear the path
 
125
  t1 = std::chrono::high_resolution_clock::now();
 
126
  pathalgorithm->Clear();
 
127
  t2 = std::chrono::high_resolution_clock::now();
 
128
  msecs = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
 
129
  LOG_INFO("PathAlgorithm Clear took " + std::to_string(msecs) + " ms");
 
130
 
 
131
  // Test RouteMatcher
 
132
  if (match_test) {
 
133
    LOG_INFO("Testing RouteMatcher");
 
134
 
 
135
    // Get shape
 
136
    std::vector<PointLL> shape = decode<std::vector<PointLL>>(trip_path.shape());
 
137
 
 
138
    // Use the shape to form a single edge correlation at the start and end of
 
139
    // the shape (using heading).
 
140
    std::vector<valhalla::baldr::Location> locations{shape.front(), shape.back()};
 
141
    locations.front().heading_ = std::round(PointLL::HeadingAlongPolyline(shape, 30.f));
 
142
    locations.back().heading_ = std::round(PointLL::HeadingAtEndOfPolyline(shape, 30.f));
 
143
 
 
144
    std::shared_ptr<DynamicCost> cost = mode_costing[static_cast<uint32_t>(mode)];
 
145
    const auto projections = Search(locations, reader, cost->GetEdgeFilter(), cost->GetNodeFilter());
 
146
    std::vector<PathLocation> path_location;
 
147
    for (auto loc : locations) {
 
148
      path_location.push_back(projections.at(loc));
 
149
    }
 
150
    std::vector<PathInfo> path;
 
151
    bool ret = RouteMatcher::FormPath(mode_costing, mode, reader, shape,
 
152
                     path_location, path);
 
153
    if (ret) {
 
154
      LOG_INFO("RouteMatcher succeeded");
 
155
    } else {
 
156
      LOG_ERROR("RouteMatcher failed");
 
157
    }
 
158
  }
 
159
 
 
160
  // Run again to see benefits of caching
 
161
  if (multi_run) {
 
162
    uint32_t totalms = 0;
 
163
    for (uint32_t i = 0; i < iterations; i++) {
 
164
      t1 = std::chrono::high_resolution_clock::now();
 
165
      pathedges = pathalgorithm->GetBestPath(origin, dest, reader, mode_costing, mode);
 
166
      t2 = std::chrono::high_resolution_clock::now();
 
167
      totalms += std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count();
 
168
      pathalgorithm->Clear();
 
169
    }
 
170
    msecs = totalms / iterations;
 
171
    LOG_INFO("PathAlgorithm GetBestPath average: " + std::to_string(msecs) + " ms");
 
172
  }
 
173
  return trip_path;
 
174
}
 
175
 
 
176
namespace std {
 
177
 
 
178
//TODO: maybe move this into location.h if its actually useful elsewhere than here?
 
179
std::string to_string(const valhalla::baldr::Location& l) {
 
180
  std::string s;
 
181
  for (auto address : { &l.name_, &l.street_, &l.city_, &l.state_, &l.zip_, &l
 
182
      .country_ }) {
 
183
    s.append(*address);
 
184
    s.push_back(',');
 
185
  }
 
186
  s.erase(s.end() - 1);
 
187
  return s;
 
188
}
 
189
 
 
190
//TODO: maybe move this into location.h if its actually useful elsewhere than here?
 
191
std::string to_json(const valhalla::baldr::Location& l) {
 
192
  std::string json = "{";
 
193
  json += "\"lat\":";
 
194
  json += std::to_string(l.latlng_.lat());
 
195
 
 
196
  json += ",\"lon\":";
 
197
  json += std::to_string(l.latlng_.lng());
 
198
 
 
199
  json += ",\"type\":\"";
 
200
  json +=
 
201
      (l.stoptype_ == valhalla::baldr::Location::StopType::THROUGH) ?
 
202
          "through" : "break";
 
203
  json += "\"";
 
204
 
 
205
  if (l.heading_) {
 
206
    json += ",\"heading\":";
 
207
    json += *l.heading_;
 
208
  }
 
209
 
 
210
  if (!l.name_.empty()) {
 
211
    json += ",\"name\":\"";
 
212
    json += l.name_;
 
213
    json += "\"";
 
214
  }
 
215
 
 
216
  if (!l.street_.empty()) {
 
217
    json += ",\"street\":\"";
 
218
    json += l.street_;
 
219
    json += "\"";
 
220
  }
 
221
 
 
222
  if (!l.city_.empty()) {
 
223
    json += ",\"city\":\"";
 
224
    json += l.city_;
 
225
    json += "\"";
 
226
  }
 
227
 
 
228
  if (!l.state_.empty()) {
 
229
    json += ",\"state\":\"";
 
230
    json += l.state_;
 
231
    json += "\"";
 
232
  }
 
233
 
 
234
  if (!l.zip_.empty()) {
 
235
    json += ",\"postal_code\":\"";
 
236
    json += l.zip_;
 
237
    json += "\"";
 
238
  }
 
239
 
 
240
  if (!l.country_.empty()) {
 
241
    json += ",\"country\":\"";
 
242
    json += l.country_;
 
243
    json += "\"";
 
244
  }
 
245
 
 
246
  json += "}";
 
247
 
 
248
  return json;
 
249
}
 
250
 
 
251
}
 
252
 
 
253
std::string GetFormattedTime(uint32_t seconds) {
 
254
  uint32_t hours = (uint32_t) seconds / 3600;
 
255
  uint32_t minutes = ((uint32_t) (seconds / 60)) % 60;
 
256
  std::string formattedTime = "";
 
257
  // Hours
 
258
  if (hours > 0) {
 
259
    formattedTime += std::to_string(hours);
 
260
    formattedTime += (hours == 1) ? " hour" : " hours";
 
261
    if (minutes > 0) {
 
262
      formattedTime += ", ";
 
263
    }
 
264
  }
 
265
  // Minutes
 
266
  if (minutes > 0) {
 
267
    formattedTime += std::to_string(minutes);
 
268
    formattedTime += (minutes == 1) ? " minute" : " minutes";
 
269
  }
 
270
  return formattedTime;
 
271
 
 
272
}
 
273
 
 
274
TripDirections DirectionsTest(const DirectionsOptions& directions_options,
 
275
                              TripPath& trip_path, const PathLocation& origin,
 
276
                              const PathLocation& destination, PathStatistics& data) {
 
277
  DirectionsBuilder directions;
 
278
  TripDirections trip_directions = directions.Build(directions_options,
 
279
                                                    trip_path);
 
280
  std::string units = (
 
281
      directions_options.units()
 
282
          == DirectionsOptions::Units::DirectionsOptions_Units_kKilometers ?
 
283
          "km" : "mi");
 
284
  int m = 1;
 
285
  valhalla::midgard::logging::Log("From: " + std::to_string(origin),
 
286
                                  " [NARRATIVE] ");
 
287
  valhalla::midgard::logging::Log("To: " + std::to_string(destination),
 
288
                                  " [NARRATIVE] ");
 
289
  valhalla::midgard::logging::Log(
 
290
      "==============================================", " [NARRATIVE] ");
 
291
  for (int i = 0; i < trip_directions.maneuver_size(); ++i) {
 
292
    const auto& maneuver = trip_directions.maneuver(i);
 
293
 
 
294
    // Depart instruction
 
295
    if (maneuver.has_depart_instruction()) {
 
296
      valhalla::midgard::logging::Log(
 
297
          (boost::format("   %s")
 
298
              % maneuver.depart_instruction()).str(),
 
299
          " [NARRATIVE] ");
 
300
    }
 
301
 
 
302
    // Verbal depart instruction
 
303
    if (maneuver.has_verbal_depart_instruction()) {
 
304
      valhalla::midgard::logging::Log(
 
305
          (boost::format("   VERBAL_DEPART: %s")
 
306
              % maneuver.verbal_depart_instruction()).str(),
 
307
          " [NARRATIVE] ");
 
308
    }
 
309
 
 
310
    // Instruction
 
311
    valhalla::midgard::logging::Log(
 
312
        (boost::format("%d: %s | %.1f %s") % m++ % maneuver.text_instruction()
 
313
            % maneuver.length() % units).str(),
 
314
        " [NARRATIVE] ");
 
315
 
 
316
    // Verbal transition alert instruction
 
317
    if (maneuver.has_verbal_transition_alert_instruction()) {
 
318
      valhalla::midgard::logging::Log(
 
319
          (boost::format("   VERBAL_ALERT: %s")
 
320
              % maneuver.verbal_transition_alert_instruction()).str(),
 
321
          " [NARRATIVE] ");
 
322
    }
 
323
 
 
324
    // Verbal pre transition instruction
 
325
    if (maneuver.has_verbal_pre_transition_instruction()) {
 
326
      valhalla::midgard::logging::Log(
 
327
          (boost::format("   VERBAL_PRE: %s")
 
328
              % maneuver.verbal_pre_transition_instruction()).str(),
 
329
          " [NARRATIVE] ");
 
330
    }
 
331
 
 
332
    // Verbal post transition instruction
 
333
    if (maneuver.has_verbal_post_transition_instruction()) {
 
334
      valhalla::midgard::logging::Log(
 
335
          (boost::format("   VERBAL_POST: %s")
 
336
              % maneuver.verbal_post_transition_instruction()).str(),
 
337
          " [NARRATIVE] ");
 
338
    }
 
339
 
 
340
    // Arrive instruction
 
341
    if (maneuver.has_arrive_instruction()) {
 
342
      valhalla::midgard::logging::Log(
 
343
          (boost::format("   %s")
 
344
              % maneuver.arrive_instruction()).str(),
 
345
          " [NARRATIVE] ");
 
346
    }
 
347
 
 
348
    // Verbal arrive instruction
 
349
    if (maneuver.has_verbal_arrive_instruction()) {
 
350
      valhalla::midgard::logging::Log(
 
351
          (boost::format("   VERBAL_ARRIVE: %s")
 
352
              % maneuver.verbal_arrive_instruction()).str(),
 
353
          " [NARRATIVE] ");
 
354
    }
 
355
 
 
356
    if (i < trip_directions.maneuver_size() - 1)
 
357
      valhalla::midgard::logging::Log(
 
358
          "----------------------------------------------", " [NARRATIVE] ");
 
359
  }
 
360
  valhalla::midgard::logging::Log(
 
361
      "==============================================", " [NARRATIVE] ");
 
362
  valhalla::midgard::logging::Log(
 
363
      "Total time: " + GetFormattedTime(trip_directions.summary().time()),
 
364
      " [NARRATIVE] ");
 
365
  valhalla::midgard::logging::Log(
 
366
      (boost::format("Total length: %.1f %s")
 
367
          % trip_directions.summary().length() % units).str(),
 
368
      " [NARRATIVE] ");
 
369
  if(origin.date_time_) {
 
370
    valhalla::midgard::logging::Log(
 
371
        "Departed at: " + *origin.date_time_,
 
372
        " [NARRATIVE] ");
 
373
  }
 
374
  if(destination.date_time_) {
 
375
    valhalla::midgard::logging::Log(
 
376
        "Arrived at: " + *destination.date_time_,
 
377
        " [NARRATIVE] ");
 
378
  }
 
379
  data.setTripTime(trip_directions.summary().time());
 
380
  data.setTripDist(trip_directions.summary().length());
 
381
  data.setManuevers(trip_directions.maneuver_size());
 
382
 
 
383
  return trip_directions;
 
384
}
 
385
 
 
386
// Returns the costing method (created from the dynamic cost factory).
 
387
// Get the costing options. Merge in any request costing options that
 
388
// override those in the config.
 
389
valhalla::sif::cost_ptr_t get_costing(CostFactory<DynamicCost> factory,
 
390
                                      boost::property_tree::ptree& request,
 
391
                                      const std::string& costing) {
 
392
  std::string method_options = "costing_options." + costing;
 
393
  auto costing_options = request.get_child(method_options, {});
 
394
  return factory.Create(costing, costing_options);
 
395
}
 
396
 
 
397
// Main method for testing a single path
 
398
int main(int argc, char *argv[]) {
 
399
  bpo::options_description options("valhalla_run_route " VERSION "\n"
 
400
  "\n"
 
401
  " Usage: valhalla_run_route [options]\n"
 
402
  "\n"
 
403
  "valhalla_run_route is a simple command line test tool for shortest path routing. "
 
404
  "\n"
 
405
  "Use the -o and -d options OR the -j option for specifying the locations. "
 
406
  "\n"
 
407
  "\n");
 
408
 
 
409
  std::string origin, destination, routetype, json, config;
 
410
  bool connectivity, multi_run, match_test;
 
411
  connectivity = multi_run = match_test = false;
 
412
  uint32_t iterations;
 
413
 
 
414
  options.add_options()("help,h", "Print this help message.")(
 
415
      "version,v", "Print the version of this software.")(
 
416
      "origin,o",
 
417
      boost::program_options::value<std::string>(&origin),
 
418
      "Origin: lat,lng,[through|stop],[name],[street],[city/town/village],[state/province/canton/district/region/department...],[zip code],[country].")(
 
419
      "destination,d",
 
420
      boost::program_options::value<std::string>(&destination),
 
421
      "Destination: lat,lng,[through|stop],[name],[street],[city/town/village],[state/province/canton/district/region/department...],[zip code],[country].")(
 
422
      "type,t", boost::program_options::value<std::string>(&routetype),
 
423
      "Route Type: auto|bicycle|pedestrian|auto-shorter")(
 
424
      "json,j",
 
425
      boost::program_options::value<std::string>(&json),
 
426
      "JSON Example: '{\"locations\":[{\"lat\":40.748174,\"lon\":-73.984984,\"type\":\"break\",\"heading\":200,\"name\":\"Empire State Building\",\"street\":\"350 5th Avenue\",\"city\":\"New York\",\"state\":\"NY\",\"postal_code\":\"10118-0110\",\"country\":\"US\"},{\"lat\":40.749231,\"lon\":-73.968703,\"type\":\"break\",\"name\":\"United Nations Headquarters\",\"street\":\"405 East 42nd Street\",\"city\":\"New York\",\"state\":\"NY\",\"postal_code\":\"10017-3507\",\"country\":\"US\"}],\"costing\":\"auto\",\"directions_options\":{\"units\":\"miles\"}}'")
 
427
      ("connectivity", "Generate a connectivity map before testing the route.")
 
428
      ("match-test", "Test RouteMatcher with resulting shape.")
 
429
      ("multi-run", bpo::value<uint32_t>(&iterations), "Generate the route N additional times before exiting.")
 
430
      // positional arguments
 
431
      ("config", bpo::value<std::string>(&config), "Valhalla configuration file");
 
432
 
 
433
 
 
434
  bpo::positional_options_description pos_options;
 
435
  pos_options.add("config", 1);
 
436
 
 
437
  bpo::variables_map vm;
 
438
 
 
439
  try {
 
440
    bpo::store(
 
441
        bpo::command_line_parser(argc, argv).options(options).positional(
 
442
            pos_options).run(),
 
443
        vm);
 
444
    bpo::notify(vm);
 
445
 
 
446
  } catch (std::exception &e) {
 
447
    std::cerr << "Unable to parse command line options because: " << e.what()
 
448
              << "\n" << "This is a bug, please report it at " PACKAGE_BUGREPORT
 
449
              << "\n";
 
450
    return EXIT_FAILURE;
 
451
  }
 
452
 
 
453
  if (vm.count("help")) {
 
454
    std::cout << options << "\n";
 
455
    return EXIT_SUCCESS;
 
456
  }
 
457
 
 
458
  if (vm.count("version")) {
 
459
    std::cout << "valhalla_run_route " << VERSION << "\n";
 
460
    return EXIT_SUCCESS;
 
461
  }
 
462
 
 
463
  if (vm.count("connectivity")) {
 
464
    connectivity = true;
 
465
  }
 
466
 
 
467
  if (vm.count("match-test")) {
 
468
    match_test = true;
 
469
  }
 
470
 
 
471
  if (vm.count("multi-run")) {
 
472
    multi_run = true;
 
473
  }
 
474
 
 
475
  // Directions options - set defaults
 
476
  DirectionsOptions directions_options;
 
477
  directions_options.set_units(
 
478
      DirectionsOptions::Units::DirectionsOptions_Units_kMiles);
 
479
  directions_options.set_language("en-US");
 
480
 
 
481
  // Locations
 
482
  std::vector<valhalla::baldr::Location> locations;
 
483
 
 
484
  // argument checking and verification
 
485
  boost::property_tree::ptree json_ptree;
 
486
  if (vm.count("json") == 0) {
 
487
    for (auto arg : std::vector<std::string> { "origin", "destination", "type",
 
488
        "config" }) {
 
489
      if (vm.count(arg) == 0) {
 
490
        std::cerr
 
491
            << "The <"
 
492
            << arg
 
493
            << "> argument was not provided, but is mandatory when json is not provided\n\n";
 
494
        std::cerr << options << "\n";
 
495
        return EXIT_FAILURE;
 
496
      }
 
497
    }
 
498
    locations.push_back(valhalla::baldr::Location::FromCsv(origin));
 
499
    locations.push_back(valhalla::baldr::Location::FromCsv(destination));
 
500
  }
 
501
  ////////////////////////////////////////////////////////////////////////////
 
502
  // Process json input
 
503
  else {
 
504
    std::stringstream stream;
 
505
    stream << json;
 
506
    boost::property_tree::read_json(stream, json_ptree);
 
507
 
 
508
    try {
 
509
      for (const auto& location : json_ptree.get_child("locations"))
 
510
        locations.emplace_back(std::move(valhalla::baldr::Location::FromPtree(location.second)));
 
511
      if (locations.size() < 2)
 
512
        throw;
 
513
    } catch (...) {
 
514
      throw std::runtime_error(
 
515
          "insufficiently specified required parameter 'locations'");
 
516
    }
 
517
 
 
518
    // Parse out the type of route - this provides the costing method to use
 
519
    std::string costing;
 
520
    try {
 
521
      routetype = json_ptree.get<std::string>("costing");
 
522
    } catch (...) {
 
523
      throw std::runtime_error("No edge/node costing provided");
 
524
    }
 
525
 
 
526
    // Grab the directions options, if they exist
 
527
    auto directions_options_ptree_ptr = json_ptree.get_child_optional(
 
528
        "directions_options");
 
529
    if (directions_options_ptree_ptr) {
 
530
      directions_options = valhalla::odin::GetDirectionsOptions(
 
531
          *directions_options_ptree_ptr);
 
532
    }
 
533
 
 
534
    // Grab the date_time, if is exists
 
535
    auto date_time_ptr = json_ptree.get_child_optional("date_time");
 
536
    if (date_time_ptr) {
 
537
      auto date_time_type = (*date_time_ptr).get<int>("type");
 
538
      auto date_time_value = (*date_time_ptr).get_optional<std::string>("value");
 
539
 
 
540
      if (date_time_type == 0) // current
 
541
        locations.front().date_time_ = "current";
 
542
      else if (date_time_type == 1) // depart at
 
543
        locations.front().date_time_ = date_time_value;
 
544
      else if (date_time_type == 2) // arrive by
 
545
        locations.back().date_time_ = date_time_value;
 
546
    }
 
547
 
 
548
  }
 
549
 
 
550
  //parse the config
 
551
  boost::property_tree::ptree pt;
 
552
  boost::property_tree::read_json(config.c_str(), pt);
 
553
 
 
554
  //configure logging
 
555
  boost::optional<boost::property_tree::ptree&> logging_subtree = pt
 
556
      .get_child_optional("thor.logging");
 
557
  if (logging_subtree) {
 
558
    auto logging_config = valhalla::midgard::ToMap<
 
559
        const boost::property_tree::ptree&,
 
560
        std::unordered_map<std::string, std::string> >(logging_subtree.get());
 
561
    valhalla::midgard::logging::Configure(logging_config);
 
562
  }
 
563
 
 
564
  // Something to hold the statistics
 
565
  uint32_t n = locations.size() - 1;
 
566
  PathStatistics data({locations[0].latlng_.lat(), locations[0].latlng_.lng()},
 
567
                      {locations[n].latlng_.lat(), locations[n].latlng_.lng()});
 
568
 
 
569
  // Crow flies distance between locations (km)
 
570
  float d1 = 0.0f;
 
571
  for (uint32_t i = 0; i < n; i++) {
 
572
    d1 += locations[i].latlng_.Distance(locations[i+1].latlng_) * kKmPerMeter;
 
573
  }
 
574
 
 
575
  // Get something we can use to fetch tiles
 
576
  valhalla::baldr::GraphReader reader(pt.get_child("mjolnir"));
 
577
 
 
578
  auto t0 = std::chrono::high_resolution_clock::now();
 
579
 
 
580
  // Construct costing
 
581
  CostFactory<DynamicCost> factory;
 
582
  factory.Register("auto", CreateAutoCost);
 
583
  factory.Register("auto_shorter", CreateAutoShorterCost);
 
584
  factory.Register("bus", CreateBusCost);
 
585
  factory.Register("bicycle", CreateBicycleCost);
 
586
  factory.Register("pedestrian", CreatePedestrianCost);
 
587
  factory.Register("truck", CreateTruckCost);
 
588
  factory.Register("transit", CreateTransitCost);
 
589
 
 
590
  // Figure out the route type
 
591
  for (auto & c : routetype)
 
592
    c = std::tolower(c);
 
593
  LOG_INFO("routetype: " + routetype);
 
594
 
 
595
  // Get the costing method - pass the JSON configuration
 
596
  TripPath trip_path;
 
597
  TravelMode mode;
 
598
  std::shared_ptr<DynamicCost> mode_costing[4];
 
599
  if (routetype == "multimodal") {
 
600
    // Create array of costing methods per mode and set initial mode to
 
601
    // pedestrian
 
602
    mode_costing[0] = get_costing(factory, json_ptree, "auto");
 
603
    mode_costing[1] = get_costing(factory, json_ptree, "pedestrian");
 
604
    mode_costing[2] = get_costing(factory, json_ptree, "bicycle");
 
605
    mode_costing[3] = get_costing(factory, json_ptree, "transit");
 
606
    mode = TravelMode::kPedestrian;
 
607
  } else {
 
608
    // Assign costing method, override any config options that are in the
 
609
    // json request
 
610
    std::shared_ptr<DynamicCost> cost = get_costing(factory,
 
611
                          json_ptree, routetype);
 
612
    mode = cost->travel_mode();
 
613
    mode_costing[static_cast<uint32_t>(mode)] = cost;
 
614
  }
 
615
 
 
616
  // Find locations
 
617
  auto t1 = std::chrono::high_resolution_clock::now();
 
618
  std::shared_ptr<DynamicCost> cost = mode_costing[static_cast<uint32_t>(mode)];
 
619
  const auto projections = Search(locations, reader, cost->GetEdgeFilter(), cost->GetNodeFilter());
 
620
  std::vector<PathLocation> path_location;
 
621
  for (auto loc : locations) {
 
622
    try {
 
623
      path_location.push_back(projections.at(loc));
 
624
      //TODO: get transit level for transit costing
 
625
      //TODO: if transit send a non zero radius
 
626
    } catch (...) {
 
627
      data.setSuccess("fail_invalid_origin");
 
628
      data.log();
 
629
      return EXIT_FAILURE;
 
630
    }
 
631
  }
 
632
  // If we are testing connectivity
 
633
  if (connectivity) {
 
634
    std::unordered_map<size_t, size_t> color_counts;
 
635
    connectivity_map_t connectivity_map(pt.get_child("mjolnir"));
 
636
    auto colors = connectivity_map.get_colors(TileHierarchy::levels().rbegin()->first,
 
637
                                              path_location.back(), 0);
 
638
    for(auto color : colors){
 
639
      auto itr = color_counts.find(color);
 
640
      if(itr == color_counts.cend())
 
641
        color_counts[color] = 1;
 
642
      else
 
643
        ++itr->second;
 
644
    }
 
645
 
 
646
    //are all the locations in the same color regions
 
647
    bool connected = false;
 
648
    for(const auto& c : color_counts) {
 
649
      if(c.second == locations.size()) {
 
650
        connected = true;
 
651
        break;
 
652
      }
 
653
    }
 
654
    if(!connected) {
 
655
      LOG_INFO("No tile connectivity between locations");
 
656
      data.setSuccess("fail_no_connectivity");
 
657
      data.log();
 
658
      return EXIT_FAILURE;
 
659
    }
 
660
  }
 
661
  auto t2 = std::chrono::high_resolution_clock::now();
 
662
  uint32_t msecs = std::chrono::duration_cast<std::chrono::milliseconds>(
 
663
                  t2 - t1).count();
 
664
  LOG_INFO("Location Processing took " + std::to_string(msecs) + " ms");
 
665
 
 
666
  // Get the route
 
667
  AStarPathAlgorithm astar;
 
668
  BidirectionalAStar bd;
 
669
  MultiModalPathAlgorithm mm;
 
670
  for (uint32_t i = 0; i < n; i++) {
 
671
    // Choose path algorithm
 
672
    PathAlgorithm* pathalgorithm;
 
673
    if (routetype == "multimodal") {
 
674
      pathalgorithm = &mm;
 
675
    } else if (routetype == "pedestrian") {
 
676
      pathalgorithm = &bd;
 
677
    } else {
 
678
      // Use bidirectional except for possible trivial cases
 
679
      pathalgorithm = &bd;
 
680
      for (auto& edge1 : path_location[i].edges) {
 
681
        for (auto& edge2 : path_location[i+1].edges) {
 
682
          if (edge1.id == edge2.id) {
 
683
            pathalgorithm = &astar;
 
684
          }
 
685
        }
 
686
      }
 
687
    }
 
688
    bool using_astar = (pathalgorithm == &astar);
 
689
 
 
690
    // Get the best path
 
691
    try {
 
692
      trip_path = PathTest(reader, path_location[i], path_location[i + 1],
 
693
                           pathalgorithm, mode_costing, mode, data, multi_run,
 
694
                           iterations, using_astar, match_test);
 
695
    } catch (std::runtime_error& rte) {
 
696
      LOG_ERROR("trip_path not found");
 
697
    }
 
698
 
 
699
    // If successful get directions
 
700
    if (trip_path.node().size() > 0) {
 
701
      // Try the the directions
 
702
      t1 = std::chrono::high_resolution_clock::now();
 
703
      TripDirections trip_directions = DirectionsTest(directions_options, trip_path,
 
704
                        path_location[i], path_location[i+1], data);
 
705
      t2 = std::chrono::high_resolution_clock::now();
 
706
      msecs = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
 
707
 
 
708
      auto trip_time = trip_directions.summary().time();
 
709
      auto trip_length = trip_directions.summary().length() * 1609.344f;
 
710
      LOG_INFO("trip_processing_time (ms)::" + std::to_string(msecs));
 
711
      LOG_INFO("trip_time (secs)::" + std::to_string(trip_time));
 
712
      LOG_INFO("trip_length (meters)::" + std::to_string(trip_length));
 
713
      data.setSuccess("success");
 
714
    } else {
 
715
      // Check if origins are unreachable
 
716
      bool unreachable_origin = false;
 
717
      for (auto& edge : path_location[i].edges) {
 
718
        const GraphTile* tile = reader.GetGraphTile(edge.id);
 
719
        const DirectedEdge* directededge = tile->directededge(edge.id);
 
720
        auto ei = tile->edgeinfo(directededge->edgeinfo_offset());
 
721
        if (directededge->unreachable()) {
 
722
          LOG_INFO("Origin edge is unconnected: wayid = " + std::to_string(ei.wayid()));
 
723
          unreachable_origin = true;
 
724
        }
 
725
        LOG_INFO("Origin wayId = " + std::to_string(ei.wayid()));
 
726
      }
 
727
 
 
728
      // Check if destinations are unreachable
 
729
      bool unreachable_dest = false;
 
730
      for (auto& edge : path_location[i+1].edges) {
 
731
        const GraphTile* tile = reader.GetGraphTile(edge.id);
 
732
        const DirectedEdge* directededge = tile->directededge(edge.id);
 
733
        auto ei = tile->edgeinfo(directededge->edgeinfo_offset());
 
734
        if (directededge->unreachable()) {
 
735
          LOG_INFO("Destination edge is unconnected: wayid = " + std::to_string(ei.wayid()));
 
736
          unreachable_dest = true;
 
737
        }
 
738
        LOG_INFO("Destination wayId = " + std::to_string(ei.wayid()));
 
739
      }
 
740
 
 
741
      // Route was unsuccessful
 
742
      if (unreachable_origin && unreachable_dest) {
 
743
        data.setSuccess("fail_unreachable_locations");
 
744
      } else if (unreachable_origin) {
 
745
        data.setSuccess("fail_unreachable_origin");
 
746
      } else if (unreachable_dest) {
 
747
        data.setSuccess("fail_unreachable_dest");
 
748
      } else {
 
749
        data.setSuccess("fail_no_route");
 
750
      }
 
751
    }
 
752
  }
 
753
 
 
754
  // Set the arc distance. Convert to miles if needed
 
755
  if (directions_options.units() == DirectionsOptions::Units::DirectionsOptions_Units_kMiles) {
 
756
    d1 *= kMilePerKm;
 
757
  }
 
758
  data.setArcDist(d1);
 
759
 
 
760
  // Time all stages for the stats file: location processing,
 
761
  // path computation, trip path building, and directions
 
762
  t2 = std::chrono::high_resolution_clock::now();
 
763
  msecs = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t0).count();
 
764
  LOG_INFO("Total time= " + std::to_string(msecs) + " ms");
 
765
  data.addRuntime(msecs);
 
766
  data.log();
 
767
 
 
768
  return EXIT_SUCCESS;
 
769
}
 
770