diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6498482 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +language: generic +sudo: false +dist: trusty + +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - cmake + +before_script: + - mkdir build + - cd build + - cmake .. + +script: + - make -j4 + - make test + +notifications: + email: + on_success: never + on_failure: never diff --git a/CMakeLists.txt b/CMakeLists.txt index abe2aa0..b17e6f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,9 +13,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/build") -find_package(Boost COMPONENTS system filesystem REQUIRED) -include_directories("build" ${Boost_INCLUDE_DIRS}) - find_package(OpenMP) if(OPENMP_FOUND) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") diff --git a/README.md b/README.md index 1241e6e..f583171 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,4 @@ -``` - _-====-__-=-__-===-__-=======-__ - _( _) - OO( ) - . o '===-______-===-____-==-__-=====' - .o - . ______ _______________ - _()_||__|| __o^o___ | [] [] [] [] | - ( | | | |o - /-OO----OO""="OO--OO"="OO---------OO" -############################################################ -``` +![Map-Matched path of a single train through Switzerland](geo/schweiz_mmatched.png?raw=true) # pfaedle @@ -18,8 +7,7 @@ Precise map-matching for public transit schedules (GTFS data). ## Requirements * `cmake` - * `gcc` >= 4.8 - * `libboost-system` >= 1.56, `libboost-filesystem` >= 1.56, `libboost-geometry` >= 1.56 + * `gcc` >= 4.8 (may work on lower versions, untested) ## Building and Installation @@ -50,16 +38,29 @@ pfaedle -c -x A shape'd version of the input GTFS feed will be written to `./gtfs-out`. -A default configuration file `pfaedle.cfg` can be found in this repo. - By default, shapes are only calculated for trips that don't have a shape in the input feed. To drop all existing shapes, use the `-D` flag. +For example, you may generate (and replace existing, see -D parameter) shapes for the GTFS dataset for Freiburg like this: + +``` +$ mkdir freiburg_gtfs && cd freiburg_gtfs +$ wget https://fritz.freiburg.de/csv_Downloads/VAGFR.zip +$ unzip VAGFR.zip +$ wget http://download.geofabrik.de/europe/germany/baden-wuerttemberg/freiburg-regbez-latest.osm.bz2 +$ bunzip2 freiburg-regbez-latest.osm.bz2 +$ mkdir gtfs-out +$ pfaedle -D -c pfaedle.cfg -x freiburg-regbez-latest.osm . +``` + +A default configuration file `pfaedle.cfg` can be found in this repo. + + ## Generating shapes for a specific MOT To generate shapes only for a specific mot, use the `-m` option. Possible values are either `tram`, `bus`, `rail`, `subway`, `ferry`, `funicular`, -`gondola`, `all`. +`gondola`, `all` (default). Multiple values can be specified (comma separated). diff --git a/geo/schweiz_mmatched.png b/geo/schweiz_mmatched.png new file mode 100644 index 0000000..d2c6b17 Binary files /dev/null and b/geo/schweiz_mmatched.png differ diff --git a/src/cppgtfs b/src/cppgtfs index 7c39553..8d25597 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit 7c395530d3c5b2d5c147fdabaaeeb0d1babceb04 +Subproject commit 8d255970a97c31891277f63ba8b39a3ea2b7e133 diff --git a/src/pfaedle/PfaedleMain.cpp b/src/pfaedle/PfaedleMain.cpp index c7d3960..2c54028 100644 --- a/src/pfaedle/PfaedleMain.cpp +++ b/src/pfaedle/PfaedleMain.cpp @@ -154,7 +154,7 @@ int main(int argc, char** argv) { if (cfg.evaluate) ecoll.printStats(&std::cout); if (cfg.feedPaths.size()) { - LOG(INFO) << "Writing output GTFS..."; + LOG(INFO) << "Writing output GTFS to " << cfg.outputPath << " ..."; ad::cppgtfs::Writer w; w.write(>fs, cfg.outputPath); } diff --git a/src/pfaedle/config/PfaedleConfig.h b/src/pfaedle/config/PfaedleConfig.h index 74047b8..ba9a2a0 100644 --- a/src/pfaedle/config/PfaedleConfig.h +++ b/src/pfaedle/config/PfaedleConfig.h @@ -21,6 +21,7 @@ struct Config { : dbgOutputPath("geo"), solveMethod("global"), evalPath("."), + outputPath("gtfs-out"), dropShapes(false), useHMM(false), writeGraph(false), diff --git a/src/pfaedle/eval/Collector.cpp b/src/pfaedle/eval/Collector.cpp index 7b162ee..334641f 100644 --- a/src/pfaedle/eval/Collector.cpp +++ b/src/pfaedle/eval/Collector.cpp @@ -118,8 +118,8 @@ double Collector::add(const Trip* t, const Shape* oldS, const Shape* newS, gjout.flush(); fstr.close(); - double fac = cos(2 * atan(exp((oldSegs.front().front().get<1>() + - oldSegs.back().back().get<1>()) / + double fac = cos(2 * atan(exp((oldSegs.front().front().getY() + + oldSegs.back().back().getY()) / 6378137.0)) - 1.5707965); @@ -401,7 +401,7 @@ std::pair Collector::getDa(const std::vector& a, // euclidean distance on web mercator is in meters on equator, // and proportional to cos(lat) in both y directions double fac = - cos(2 * atan(exp((a.front().front().get<1>() + a.back().back().get<1>()) / + cos(2 * atan(exp((a.front().front().getY() + a.back().back().getY()) / 6378137.0)) - 1.5707965); diff --git a/src/pfaedle/osm/BBoxIdx.cpp b/src/pfaedle/osm/BBoxIdx.cpp index 8344a55..e5d7c08 100644 --- a/src/pfaedle/osm/BBoxIdx.cpp +++ b/src/pfaedle/osm/BBoxIdx.cpp @@ -31,10 +31,10 @@ bool BBoxIdx::contains(const Point& p) const { // _____________________________________________________________________________ util::geo::Box BBoxIdx::getFullWebMercBox() const { return util::geo::FBox( - util::geo::latLngToWebMerc(_root.box.min_corner().get<1>(), - _root.box.min_corner().get<0>()), - util::geo::latLngToWebMerc(_root.box.max_corner().get<1>(), - _root.box.max_corner().get<0>())); + util::geo::latLngToWebMerc(_root.box.getLowerLeft().getY(), + _root.box.getLowerLeft().getX()), + util::geo::latLngToWebMerc(_root.box.getUpperRight().getY(), + _root.box.getUpperRight().getX())); } // _____________________________________________________________________________ diff --git a/src/pfaedle/osm/OsmBuilder.cpp b/src/pfaedle/osm/OsmBuilder.cpp index bb3ffd4..455bb1d 100644 --- a/src/pfaedle/osm/OsmBuilder.cpp +++ b/src/pfaedle/osm/OsmBuilder.cpp @@ -991,7 +991,7 @@ double OsmBuilder::webMercDistFactor(const util::geo::FPoint& a) const { // euclidean distance on web mercator is in meters on equator, // and proportional to cos(lat) in both y directions - double lat = 2 * atan(exp(a.get<1>() / 6378137.0)) - 1.5707965; + double lat = 2 * atan(exp(a.getY() / 6378137.0)) - 1.5707965; return cos(lat); } diff --git a/src/pfaedle/router/Router.cpp b/src/pfaedle/router/Router.cpp index b96aaf6..5cac1cb 100644 --- a/src/pfaedle/router/Router.cpp +++ b/src/pfaedle/router/Router.cpp @@ -121,8 +121,8 @@ NDistHeur::NDistHeur(const RoutingOpts& rOpts, size_t c = 0; double x = 0, y = 0; for (auto to : tos) { - x += to->pl().getGeom()->get<0>(); - y += to->pl().getGeom()->get<1>(); + x += to->pl().getGeom()->getX(); + y += to->pl().getGeom()->getY(); c++; } @@ -144,8 +144,8 @@ DistHeur::DistHeur(uint8_t minLvl, const RoutingOpts& rOpts, size_t c = 0; double x = 0, y = 0; for (auto to : tos) { - x += to->getFrom()->pl().getGeom()->get<0>(); - y += to->getFrom()->pl().getGeom()->get<1>(); + x += to->getFrom()->pl().getGeom()->getX(); + y += to->getFrom()->pl().getGeom()->getY(); c++; } diff --git a/src/pfaedle/router/ShapeBuilder.cpp b/src/pfaedle/router/ShapeBuilder.cpp index a85286a..ede3c64 100644 --- a/src/pfaedle/router/ShapeBuilder.cpp +++ b/src/pfaedle/router/ShapeBuilder.cpp @@ -313,8 +313,8 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape( for (const auto& hop : shp.hops) { const trgraph::Node* l = hop.start; if (hop.edges.size() == 0) { - FPoint ll = webMercToLatLng(hop.start->pl().getGeom()->get<0>(), - hop.start->pl().getGeom()->get<1>()); + FPoint ll = webMercToLatLng(hop.start->pl().getGeom()->getX(), + hop.start->pl().getGeom()->getY()); if (dist > -0.5) dist += webMercMeterDist(last, *hop.start->pl().getGeom()); @@ -324,7 +324,7 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape( last = *hop.start->pl().getGeom(); if (dist - lastDist > 0.01) { - ret->addPoint(ShapePoint(ll.get<1>(), ll.get<0>(), dist, seq)); + ret->addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); seq++; lastDist = dist; } @@ -333,9 +333,9 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape( last = *hop.end->pl().getGeom(); if (dist - lastDist > 0.01) { - ll = webMercToLatLng(hop.end->pl().getGeom()->get<0>(), - hop.end->pl().getGeom()->get<1>()); - ret->addPoint(ShapePoint(ll.get<1>(), ll.get<0>(), dist, seq)); + ll = webMercToLatLng(hop.end->pl().getGeom()->getX(), + hop.end->pl().getGeom()->getY()); + ret->addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); seq++; lastDist = dist; } @@ -351,8 +351,8 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape( dist = 0; last = cur; if (dist - lastDist > 0.01) { - FPoint ll = webMercToLatLng(cur.get<0>(), cur.get<1>()); - ret->addPoint(ShapePoint(ll.get<1>(), ll.get<0>(), dist, seq)); + FPoint ll = webMercToLatLng(cur.getX(), cur.getY()); + ret->addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); seq++; lastDist = dist; } @@ -366,8 +366,8 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape( dist = 0; last = cur; if (dist - lastDist > 0.01) { - FPoint ll = webMercToLatLng(cur.get<0>(), cur.get<1>()); - ret->addPoint(ShapePoint(ll.get<1>(), ll.get<0>(), dist, seq)); + FPoint ll = webMercToLatLng(cur.getX(), cur.getY()); + ret->addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); seq++; lastDist = dist; } diff --git a/src/util/Misc.h b/src/util/Misc.h index 1857db9..eecde29 100644 --- a/src/util/Misc.h +++ b/src/util/Misc.h @@ -5,6 +5,7 @@ #ifndef UTIL_MISC_H_ #define UTIL_MISC_H_ +#include #include #define UNUSED(expr) do { (void)(expr); } while (0) diff --git a/src/util/geo/BezierCurve.h b/src/util/geo/BezierCurve.h index 29f669d..f3d25c5 100644 --- a/src/util/geo/BezierCurve.h +++ b/src/util/geo/BezierCurve.h @@ -47,7 +47,7 @@ class BezierCurve { Point valueAt(double t) const; }; -#include "util/geo/PolyLine.tpp" +#include "util/geo/BezierCurve.tpp" } } diff --git a/src/util/geo/BezierCurve.tpp b/src/util/geo/BezierCurve.tpp index 6e7ed14..fb7e6ca 100644 --- a/src/util/geo/BezierCurve.tpp +++ b/src/util/geo/BezierCurve.tpp @@ -4,8 +4,8 @@ // _____________________________________________________________________________ template -BezierCurve::BezierCurve(const Point& a, const Point& b, const Point& c, - const Point& d) +BezierCurve::BezierCurve(const Point& a, const Point& b, + const Point& c, const Point& d) : _d(dist(a, d)) { assert(_d > 0); recalcPolynoms(a, b, c, d); @@ -13,17 +13,17 @@ BezierCurve::BezierCurve(const Point& a, const Point& b, const Point // _____________________________________________________________________________ template -void BezierCurve::recalcPolynoms(const Point& a, const Point& b, const Point& c, - const Point& d) { - _xp.a = a.template get<0>(); - _xp.b = 3.0 * (b.template get<0>() - a.template get<0>()); - _xp.c = 3.0 * (c.template get<0>() - b.template get<0>()) - _xp.b; - _xp.d = d.template get<0>() - a.template get<0>() - _xp.c - _xp.b; +void BezierCurve::recalcPolynoms(const Point& a, const Point& b, + const Point& c, const Point& d) { + _xp.a = a.getX(); + _xp.b = 3.0 * (b.getX() - a.getX()); + _xp.c = 3.0 * (c.getX() - b.getX()) - _xp.b; + _xp.d = d.getX() - a.getX() - _xp.c - _xp.b; - _yp.a = a.template get<1>(); - _yp.b = 3.0 * (b.template get<1>() - a.template get<1>()); - _yp.c = 3.0 * (c.template get<1>() - b.template get<1>()) - _yp.b; - _yp.d = d.template get<1>() - a.template get<1>() - _yp.c - _yp.b; + _yp.a = a.getY(); + _yp.b = 3.0 * (b.getY() - a.getY()); + _yp.c = 3.0 * (c.getY() - b.getY()) - _yp.b; + _yp.d = d.getY() - a.getY() - _yp.c - _yp.b; _didRender = false; } diff --git a/src/util/geo/Box.h b/src/util/geo/Box.h new file mode 100644 index 0000000..f2e77de --- /dev/null +++ b/src/util/geo/Box.h @@ -0,0 +1,89 @@ +// Copyright 2016, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Patrick Brosi + +#ifndef UTIL_GEO_BOX_H_ +#define UTIL_GEO_BOX_H_ + +#include "./Point.h" + +namespace util { +namespace geo { + +template +class Box { + public: + // maximum inverse box as default value of box + Box() + : _ll(std::numeric_limits::max(), std::numeric_limits::max()), + _ur(std::numeric_limits::min(), std::numeric_limits::min()) {} + Box(const Point& ll, const Point& ur) : _ll(ll), _ur(ur) {} + const Point& getLowerLeft() const { return _ll; } + const Point& getUpperRight() const { return _ur; } + + Point& getLowerLeft() { return _ll; } + Point& getUpperRight() { return _ur; } + + void setLowerLeft(const Point& ll) { _ll = ll; } + void setUpperRight(const Point& ur) { _ur = ur; } + + bool operator==(const Box& b) const { + return getLowerLeft() == b.getLowerLeft() && + getUpperRight == b.getUpperRight(); + } + + bool operator!=(const Box& p) const { return !(*this == p); } + + private: + Point _ll, _ur; +}; + +template +class RotatedBox { + public: + RotatedBox() : _box(), _deg(0), _center() {} + RotatedBox(const Box& box) + : _box(box), + _deg(0), + _center(Point( + (box.getUpperRight().getX() - box.getLowerLeft().getX()) / T(2), + (box.getUpperRight().getY() - box.getLowerLeft().getY()) / T(2))) {} + RotatedBox(const Point& ll, const Point& ur) + : _box(ll, ur), + _deg(0), + _center(Point((ur.getX() - ll.getX()) / T(2), + (ur.getY() - ll.getY()) / T(2))) {} + RotatedBox(const Box& box, double deg) + : _box(box), + _deg(deg), + _center(Point( + (box.getUpperRight().getX() - box.getLowerLeft().getX()) / T(2), + (box.getUpperRight().getY() - box.getLowerLeft().getY()) / T(2))) {} + RotatedBox(const Point& ll, const Point& ur, double deg) + : _box(ll, ur), + _deg(deg), + _center(Point((ur.getX() - ll.getX()) / T(2), + (ur.getY() - ll.getY()) / T(2))) {} + RotatedBox(const Box& box, double deg, const Point& center) + : _box(box), _deg(deg), _center(center) {} + RotatedBox(const Point& ll, const Point& ur, double deg, + const Point& center) + : _box(ll, ur), _deg(deg), _center(center) {} + + const Box& getBox() const { return _box; } + Box& getBox() { return _box; } + + double getDegree() const { return _deg; } + const Point& getCenter() const { return _center; } + Point& getCenter() { return _center; } + + private: + Box _box; + double _deg; + Point _center; +}; + +} // namespace geo +} // namespace util + +#endif // UTIL_GEO_BOX_H_ diff --git a/src/util/geo/Geo.h b/src/util/geo/Geo.h index db34ed4..a020c55 100644 --- a/src/util/geo/Geo.h +++ b/src/util/geo/Geo.h @@ -1,49 +1,40 @@ // Copyright 2016, University of Freiburg, // Chair of Algorithms and Data Structures. // Authors: Patrick Brosi + #ifndef UTIL_GEO_GEO_H_ #define UTIL_GEO_GEO_H_ #define _USE_MATH_DEFINES #include -#include -#include +#include +#include +#include +#include #include "util/Misc.h" +#include "util/geo/Box.h" +#include "util/geo/Line.h" +#include "util/geo/Point.h" +#include "util/geo/Polygon.h" // ------------------- // Geometry stuff // ------------------ -namespace bgeo = boost::geometry; - namespace util { namespace geo { -template -using Point = bgeo::model::point; - -template -using Line = bgeo::model::linestring>; - -template -using MultiLine = bgeo::model::multi_linestring>; - -template -using Polygon = bgeo::model::polygon>; - -template -using MultiPolygon = bgeo::model::multi_polygon>; - -template -using Box = bgeo::model::box>; - // convenience aliases typedef Point DPoint; typedef Point FPoint; typedef Point IPoint; +typedef LineSegment DLineSegment; +typedef LineSegment FLineSegment; +typedef LineSegment ILineSegment; + typedef Line DLine; typedef Line FLine; typedef Line ILine; @@ -52,193 +43,486 @@ typedef Box DBox; typedef Box FBox; typedef Box IBox; -// _____________________________________________________________________________ -template -inline Line rotate(const Line& geo, double deg, const Point& center) { - Line ret; +typedef Polygon DPolygon; +typedef Polygon FPolygon; +typedef Polygon IPolygon; - bgeo::strategy::transform::translate_transformer translate( - -center.template get<0>(), -center.template get<1>()); - bgeo::strategy::transform::rotate_transformer rotate( - deg); - bgeo::strategy::transform::translate_transformer translateBack( - center.template get<0>(), center.template get<1>()); - - bgeo::strategy::transform::ublas_transformer translateRotate( - prod(rotate.matrix(), translate.matrix())); - bgeo::strategy::transform::ublas_transformer all( - prod(translateBack.matrix(), translateRotate.matrix())); - - bgeo::transform(geo, ret, all); - - return ret; -} - -// _____________________________________________________________________________ -template -inline MultiLine rotate(const MultiLine& geo, double deg, - const Point& center) { - MultiLine ret; - - bgeo::strategy::transform::translate_transformer translate( - -center.template get<0>(), -center.template get<1>()); - bgeo::strategy::transform::rotate_transformer rotate( - deg); - bgeo::strategy::transform::translate_transformer translateBack( - center.template get<0>(), center.template get<1>()); - - bgeo::strategy::transform::ublas_transformer translateRotate( - prod(rotate.matrix(), translate.matrix())); - bgeo::strategy::transform::ublas_transformer all( - prod(translateBack.matrix(), translateRotate.matrix())); - - bgeo::transform(geo, ret, all); - - return ret; -} +const static double EPSILON = 0.00000000001; +const static double RAD = 0.017453292519943295; // PI/180 // _____________________________________________________________________________ template inline Box pad(const Box& box, double padding) { - return Box(Point(box.min_corner().template get<0>() - padding, - box.min_corner().template get<1>() - padding), - Point(box.max_corner().template get<0>() + padding, - box.max_corner().template get<1>() + padding)); + return Box(Point(box.getLowerLeft().getX() - padding, + box.getLowerLeft().getY() - padding), + Point(box.getUpperRight().getX() + padding, + box.getUpperRight().getY() + padding)); } // _____________________________________________________________________________ template -inline Line rotate(const Line& geo, double deg) { - Point center; - bgeo::centroid(geo, center); - return rotate(geo, deg, center); +inline Point centroid(const Point p) { + return p; } // _____________________________________________________________________________ template -inline MultiLine rotate(const MultiLine& geo, double deg) { - Point center; - bgeo::centroid(geo, center); - return rotate(geo, deg, center); +inline Point centroid(const LineSegment ls) { + return Point((ls.first.getX() + ls.second.getX()) / T(2), + (ls.first.getY() + ls.second.getY()) / T(2)); } // _____________________________________________________________________________ -template