From b3abf8f8574299724bab7bd9b9a1bebd511a08da Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 10:56:48 -0600 Subject: [PATCH 01/14] [CI][windows] window-latest is using VS 17 2022 --- .github/workflows/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index a59180ed13b..e44d61482f2 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -51,7 +51,7 @@ jobs: run: | mkdir build cd build - cmake -DPOSTGRESQL_BIN=${{ env.POSTGRESQL_DIR }}\bin -DPOSTGRESQL_INCLUDE_DIR="${{ env.POSTGRESQL_DIR }}\include;${{ env.POSTGRESQL_DIR }}\include\server" -G"Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Release -DWITH_DOC=OFF .. + cmake -DPOSTGRESQL_BIN=${{ env.POSTGRESQL_DIR }}\bin -DPOSTGRESQL_INCLUDE_DIR="${{ env.POSTGRESQL_DIR }}\include;${{ env.POSTGRESQL_DIR }}\include\server" -G"Visual Studio 17 2022" -DCMAKE_BUILD_TYPE=Release -DWITH_DOC=OFF .. env: BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} From 5513943b592d5da30309d3894ea281beebf0771b Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 10:17:40 -0600 Subject: [PATCH 02/14] [src][trspVia_withPoints] adding new files --- .../drivers/trsp/trspVia_withPoints_driver.h | 77 +++++ src/trsp/CMakeLists.txt | 3 + src/trsp/trspVia_withPoints.c | 239 ++++++++++++++ src/trsp/trspVia_withPoints_driver.cpp | 302 ++++++++++++++++++ 4 files changed, 621 insertions(+) create mode 100644 include/drivers/trsp/trspVia_withPoints_driver.h create mode 100644 src/trsp/trspVia_withPoints.c create mode 100644 src/trsp/trspVia_withPoints_driver.cpp diff --git a/include/drivers/trsp/trspVia_withPoints_driver.h b/include/drivers/trsp/trspVia_withPoints_driver.h new file mode 100644 index 00000000000..c187b1cf8de --- /dev/null +++ b/include/drivers/trsp/trspVia_withPoints_driver.h @@ -0,0 +1,77 @@ +/*PGR-GNU***************************************************************** +File: trspiVia_withPoints_driver.h + +Function's developer: +Copyright (c) 2022 Celia Virginia Vergara Castillo + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +#ifndef INCLUDE_DRIVERS_TRSP_TRSPVIA_WITHPOINTS_DRIVER_H_ +#define INCLUDE_DRIVERS_TRSP_TRSPVIA_WITHPOINTS_DRIVER_H_ +#pragma once + +#ifdef __cplusplus +# include +# include +using Point_on_edge_t = struct Point_on_edge_t; +using Edge_t = struct Edge_t; +using Routes_t = struct Routes_t; +using Restriction_t = struct Restriction_t; +#else +# include +# include +typedef struct Point_on_edge_t Point_on_edge_t; +typedef struct Edge_t Edge_t; +typedef struct Routes_t Routes_t; +typedef struct Restriction_t Restriction_t; +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Process pgr_trsp_withPointsVia */ +void +do_trspVia_withPoints( + Edge_t*, size_t, // edges + Restriction_t *, size_t, // restrictions + Point_on_edge_t *, size_t, // Points + Edge_t*, size_t, // edges of points + int64_t *, size_t, // via vertices + + bool, // directed + + char, // driving_side + bool, // details + + bool, // strict + bool, // U_turn_on_edge, + + Routes_t**, size_t*, // results + + char**, // log + char**, // notice + char**); // error + +#ifdef __cplusplus +} +#endif + +#endif // INCLUDE_DRIVERS_TRSP_TRSPVIA_WITHPOINTS_DRIVER_H_ diff --git a/src/trsp/CMakeLists.txt b/src/trsp/CMakeLists.txt index 43867e41b56..533f3e2ed6f 100644 --- a/src/trsp/CMakeLists.txt +++ b/src/trsp/CMakeLists.txt @@ -14,4 +14,7 @@ add_library(trsp OBJECT trspVia.c trspVia_driver.cpp + + trspVia_withPoints.c + trspVia_withPoints_driver.cpp ) diff --git a/src/trsp/trspVia_withPoints.c b/src/trsp/trspVia_withPoints.c new file mode 100644 index 00000000000..3dbe5886301 --- /dev/null +++ b/src/trsp/trspVia_withPoints.c @@ -0,0 +1,239 @@ +/*PGR-GNU***************************************************************** +File: trspVia_withPoints.c + +Function's developer: +Copyright (c) 2022 Celia Virginia Vergara Castillo + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +#include +#include "c_common/postgres_connection.h" +#include "utils/array.h" +#include "c_types/routes_t.h" +#include "c_common/debug_macro.h" +#include "c_common/e_report.h" +#include "c_common/time_msg.h" +#include "c_common/edges_input.h" +#include "c_common/arrays_input.h" +#include "c_common/restrictions_input.h" +#include "c_common/points_input.h" +#include "drivers/withPoints/get_new_queries.h" +#include "drivers/trsp/trspVia_withPoints_driver.h" + +PGDLLEXPORT Datum _pgr_trspvia_withpoints(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(_pgr_trspvia_withpoints); + +static +void +process( + char* edges_sql, + char* restrictions_sql, + char* points_sql, + ArrayType *viasArr, + bool directed, + + bool strict, + bool U_turn_on_edge, + + char *driving_side, + bool details, + + Routes_t **result_tuples, + size_t *result_count) { + pgr_SPI_connect(); + + /* + * Processing Via + */ + size_t size_via = 0; + int64_t* via = (int64_t*) pgr_get_bigIntArray(&size_via, viasArr); + + // TODO(vicky) figure out what happens when one point or 0 points are given + + /* + * Processing Points + */ + driving_side[0] = estimate_drivingSide(driving_side[0]); + if (driving_side[0] != 'r' && driving_side[0] != 'l') { + driving_side[0] = 'l'; + } + + Point_on_edge_t *points = NULL; + size_t total_points = 0; + pgr_get_points(points_sql, &points, &total_points); + + char *edges_of_points_query = NULL; + char *edges_no_points_query = NULL; + get_new_queries(edges_sql, points_sql, &edges_of_points_query, &edges_no_points_query); + + /* + * Processing Edges + */ + Edge_t *edges_of_points = NULL; + size_t total_edges_of_points = 0; + + Edge_t *edges = NULL; + size_t total_edges = 0; + + pgr_get_edges(edges_no_points_query, &edges, &total_edges); + pgr_get_edges(edges_of_points_query, &edges_of_points, &total_edges_of_points); + + {pfree(edges_of_points_query); edges_of_points_query = NULL;} + {pfree(edges_no_points_query); edges_no_points_query = NULL;} + + if ((total_edges + total_edges_of_points) == 0) { + if (edges) {pfree(edges); edges = NULL;} + if (edges_of_points) {pfree(edges_of_points); edges_of_points = NULL;} + if (via) {pfree(via); via = NULL;} + pgr_SPI_finish(); + return; + } + + /* + * Processing restrictions + */ + Restriction_t * restrictions = NULL; + size_t size_restrictions = 0; + + pgr_get_restrictions(restrictions_sql, &restrictions, &size_restrictions); + + clock_t start_t = clock(); + char* log_msg = NULL; + char* notice_msg = NULL; + char* err_msg = NULL; + do_trspVia_withPoints( + edges, total_edges, + restrictions, size_restrictions, + points, total_points, + edges_of_points, total_edges_of_points, + via, size_via, + + directed, + + driving_side[0], + details, + + strict, + U_turn_on_edge, + + result_tuples, result_count, + &log_msg, ¬ice_msg, &err_msg); + time_msg("processing pgr_trsp_withPointsVia", start_t, clock()); + + if (err_msg && (*result_tuples)) { + pfree(*result_tuples); + (*result_tuples) = NULL; + (*result_count) = 0; + } + + pgr_global_report(log_msg, notice_msg, err_msg); + + if (log_msg) {pfree(log_msg); log_msg = NULL;} + if (notice_msg) {pfree(notice_msg); notice_msg = NULL;} + if (err_msg) {pfree(err_msg); err_msg = NULL;} + if (edges) {pfree(edges); edges = NULL;} + if (via) {pfree(via); via = NULL;} + if (restrictions) {pfree(restrictions); restrictions = NULL;} + if (edges_of_points) {pfree(edges_of_points); edges_of_points = NULL;} + if (points) {pfree(points); points = NULL;} + pgr_SPI_finish(); +} + + +PGDLLEXPORT Datum +_pgr_trspvia_withpoints(PG_FUNCTION_ARGS) { + FuncCallContext *funcctx; + TupleDesc tuple_desc; + + Routes_t *result_tuples = 0; + size_t result_count = 0; + + if (SRF_IS_FIRSTCALL()) { + MemoryContext oldcontext; + funcctx = SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + + process( + text_to_cstring(PG_GETARG_TEXT_P(0)), + text_to_cstring(PG_GETARG_TEXT_P(1)), + text_to_cstring(PG_GETARG_TEXT_P(2)), + PG_GETARG_ARRAYTYPE_P(3), + PG_GETARG_BOOL(4), + + PG_GETARG_BOOL(5), + PG_GETARG_BOOL(6), + + text_to_cstring(PG_GETARG_TEXT_P(7)), + PG_GETARG_BOOL(8), + + &result_tuples, + &result_count); + + funcctx->max_calls = result_count; + funcctx->user_fctx = result_tuples; + + if (get_call_result_type(fcinfo, NULL, &tuple_desc) + != TYPEFUNC_COMPOSITE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function returning record called in context " + "that cannot accept type record"))); + + funcctx->tuple_desc = tuple_desc; + MemoryContextSwitchTo(oldcontext); + } + + funcctx = SRF_PERCALL_SETUP(); + tuple_desc = funcctx->tuple_desc; + result_tuples = (Routes_t*) funcctx->user_fctx; + + if (funcctx->call_cntr < funcctx->max_calls) { + HeapTuple tuple; + Datum result; + Datum *values; + bool* nulls; + size_t call_cntr = funcctx->call_cntr; + + size_t numb_out = 10; + values = palloc(numb_out * sizeof(Datum)); + nulls = palloc(numb_out * sizeof(bool)); + size_t i; + for (i = 0; i< numb_out; ++i) { + nulls[i] = false; + } + + values[0] = Int32GetDatum(call_cntr + 1); + values[1] = Int32GetDatum(result_tuples[call_cntr].path_id); + values[2] = Int32GetDatum(result_tuples[call_cntr].path_seq + 1); + values[3] = Int64GetDatum(result_tuples[call_cntr].start_vid); + values[4] = Int64GetDatum(result_tuples[call_cntr].end_vid); + values[5] = Int64GetDatum(result_tuples[call_cntr].node); + values[6] = Int64GetDatum(result_tuples[call_cntr].edge); + values[7] = Float8GetDatum(result_tuples[call_cntr].cost); + values[8] = Float8GetDatum(result_tuples[call_cntr].agg_cost); + values[9] = Float8GetDatum(result_tuples[call_cntr].route_agg_cost); + + tuple = heap_form_tuple(tuple_desc, values, nulls); + result = HeapTupleGetDatum(tuple); + SRF_RETURN_NEXT(funcctx, result); + } else { + SRF_RETURN_DONE(funcctx); + } +} diff --git a/src/trsp/trspVia_withPoints_driver.cpp b/src/trsp/trspVia_withPoints_driver.cpp new file mode 100644 index 00000000000..c65b96ebc40 --- /dev/null +++ b/src/trsp/trspVia_withPoints_driver.cpp @@ -0,0 +1,302 @@ +/*PGR-GNU***************************************************************** +File: trspVia_withPoints_driver.cpp + +Function's developer: +Copyright (c) 2022 Celia Virginia Vergara Castillo + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +#include "drivers/trsp/trspVia_withPoints_driver.h" + +#include +#include +#include +#include + +#include "dijkstra/pgr_dijkstraVia.hpp" +#include "withPoints/pgr_withPoints.hpp" +#include "c_types/routes_t.h" +#include "c_types/restriction_t.h" +#include "cpp_common/rule.h" +#include "cpp_common/combinations.h" +#include "cpp_common/pgr_alloc.hpp" +#include "cpp_common/pgr_assert.h" + +#include "trsp/pgr_trspHandler.h" + + +namespace { + +/** @brief Orders results in terms of the via information */ +void +post_process_trspvia(std::deque &paths, std::vector via) { + for (auto &p : paths) { + p.recalculate_agg_cost(); + } + + std::deque ordered_paths; + auto u = via.front(); + bool skip = true; + for (const auto &v : via) { + if (skip) {skip = false; continue;} + /* + * look for the path (u,v) + */ + auto path_ptr = std::find_if( + paths.begin(), paths.end(), + [&](const Path &path) + {return (u == path.start_id()) && (v == path.end_id());}); + + if (path_ptr == paths.end()) { + /* + * TODO path not found + */ + } else { + /* path was found */ + ordered_paths.push_back(*path_ptr); + paths.erase(path_ptr); + } + u = v; + } + + paths = ordered_paths; +} + +void +get_path( + int route_id, + int path_id, + const Path &path, + Routes_t **postgres_data, + double &route_cost, + size_t &sequence) { + size_t i = 0; + for (const auto e : path) { + (*postgres_data)[sequence] = { + route_id, + path_id, + static_cast(i), + path.start_id(), + path.end_id(), + e.node, + e.edge, + e.cost, + e.agg_cost, + route_cost}; + route_cost += path[i].cost; + ++i; + ++sequence; + } +} + +size_t +get_route( + Routes_t **ret_path, + std::deque &paths) { + size_t sequence = 0; + int path_id = 1; + int route_id = 1; + double route_cost = 0; // routes_agg_cost + for (auto &p : paths) { + p.recalculate_agg_cost(); + } + for (const Path &path : paths) { + if (path.size() > 0) + get_path(route_id, path_id, path, ret_path, route_cost, sequence); + ++path_id; + } + return sequence; +} +} // namespace + +void +do_trspVia_withPoints( + Edge_t* edges, size_t total_edges, + Restriction_t *restrictions, size_t restrictions_size, + Point_on_edge_t *points_p, size_t total_points, + Edge_t *edges_of_points, size_t total_edges_of_points, + int64_t* via_vidsArr, size_t size_via_vidsArr, + + bool directed, + + char driving_side, + bool details, + + bool strict, + bool U_turn_on_edge, + + Routes_t** return_tuples, size_t* return_count, + + char** log_msg, + char** notice_msg, + char** err_msg) { + std::ostringstream log; + std::ostringstream err; + std::ostringstream notice; + + try { + pgassert(total_edges != 0); + pgassert(!(*log_msg)); + pgassert(!(*notice_msg)); + pgassert(!(*err_msg)); + pgassert(!(*return_tuples)); + pgassert(*return_count == 0); + + std::dequepaths; + + graphType gType = directed? DIRECTED: UNDIRECTED; + + /* + * processing via + */ + std::vector via_vertices(via_vidsArr, via_vidsArr + size_via_vidsArr); + + /* + * processing points + */ + pgrouting::Pg_points_graph pg_graph( + std::vector( + points_p, + points_p + total_points), + std::vector< Edge_t >( + edges_of_points, + edges_of_points + total_edges_of_points), + true, + driving_side, + directed); + + if (pg_graph.has_error()) { + log << pg_graph.get_log(); + err << pg_graph.get_error(); + *log_msg = pgr_msg(log.str().c_str()); + *err_msg = pgr_msg(err.str().c_str()); + return; + } + + auto vertices(pgrouting::extract_vertices(edges, total_edges)); + vertices = pgrouting::extract_vertices(vertices, pg_graph.new_edges()); + + if (directed) { + pgrouting::DirectedGraph digraph(vertices, gType); + digraph.insert_edges(edges, total_edges); + digraph.insert_edges(pg_graph.new_edges()); + pgrouting::pgr_dijkstraVia( + digraph, + via_vertices, + paths, + strict, + U_turn_on_edge, + log); + } else { + pgrouting::UndirectedGraph undigraph(vertices, gType); + undigraph.insert_edges(edges, total_edges); + undigraph.insert_edges(pg_graph.new_edges()); + pgrouting::pgr_dijkstraVia( + undigraph, + via_vertices, + paths, + strict, + U_turn_on_edge, + log); + } + + if (!details) { + for (auto &path : paths) path = pg_graph.eliminate_details(path); + } + + size_t count(count_tuples(paths)); + + if (count == 0) { + notice << "No paths found"; + *log_msg = pgr_msg(notice.str().c_str()); + return; + } + + if (restrictions_size == 0) { + (*return_tuples) = pgr_alloc(count, (*return_tuples)); + (*return_count) = (get_route(return_tuples, paths)); + (*return_tuples)[count - 1].edge = -2; + return; + } + + /* + * When there are turn restrictions + */ + std::vector ruleList; + for (size_t i = 0; i < restrictions_size; ++i) { + if (restrictions[i].via_size == 0) continue; + ruleList.push_back(pgrouting::trsp::Rule(*(restrictions + i))); + } + + auto new_combinations = pgrouting::utilities::get_combinations(paths, ruleList); + + if (!new_combinations.empty()) { + pgrouting::trsp::Pgr_trspHandler gdef( + edges, + total_edges, + pg_graph.new_edges(), + directed, + ruleList); + auto new_paths = gdef.process(new_combinations); + paths.insert(paths.end(), new_paths.begin(), new_paths.end()); + } + post_process_trspvia(paths, via_vertices); + if (!details) { + for (auto &path : paths) path = pg_graph.eliminate_details(path); + } + + + count = count_tuples(paths); + + if (count == 0) { + (*return_tuples) = NULL; + (*return_count) = 0; + return; + } + + (*return_tuples) = pgr_alloc(count, (*return_tuples)); + (*return_count) = (get_route(return_tuples, paths)); + (*return_tuples)[count - 1].edge = -2; + + *log_msg = log.str().empty()? + *log_msg : + pgr_msg(log.str().c_str()); + *notice_msg = notice.str().empty()? + *notice_msg : + pgr_msg(notice.str().c_str()); + } catch (AssertFailedException &except) { + (*return_tuples) = pgr_free(*return_tuples); + (*return_count) = 0; + err << except.what(); + *err_msg = pgr_msg(err.str().c_str()); + *log_msg = pgr_msg(log.str().c_str()); + } catch (std::exception &except) { + (*return_tuples) = pgr_free(*return_tuples); + (*return_count) = 0; + err << except.what(); + *err_msg = pgr_msg(err.str().c_str()); + *log_msg = pgr_msg(log.str().c_str()); + } catch(...) { + (*return_tuples) = pgr_free(*return_tuples); + (*return_count) = 0; + err << "Caught unknown exception!"; + *err_msg = pgr_msg(err.str().c_str()); + *log_msg = pgr_msg(log.str().c_str()); + } +} From 0f8fc50b47cefeb146fdf47cf2d49fca46c080d6 Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 10:24:58 -0600 Subject: [PATCH 03/14] [sql][trspVia_withPoints] adding postgres functions --- sql/sigs/pgrouting--3.4.sig | 2 + sql/trsp/CMakeLists.txt | 3 ++ sql/trsp/_trspVia_withPoints.sql | 56 +++++++++++++++++++++ sql/trsp/trspVia_withPoints.sql | 83 ++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 sql/trsp/_trspVia_withPoints.sql create mode 100644 sql/trsp/trspVia_withPoints.sql diff --git a/sql/sigs/pgrouting--3.4.sig b/sql/sigs/pgrouting--3.4.sig index 4d7e998d6d6..a771c60399d 100644 --- a/sql/sigs/pgrouting--3.4.sig +++ b/sql/sigs/pgrouting--3.4.sig @@ -266,6 +266,8 @@ _pgr_trspvia(text,text,anyarray,boolean,boolean,boolean) pgr_trspvia(text,text,anyarray,boolean,boolean,boolean) pgr_trspviavertices(text,anyarray,boolean,boolean,text) _pgr_trspviavertices(text,integer[],boolean,boolean,text) +_pgr_trspvia_withpoints(text,text,text,anyarray,boolean,boolean,boolean,character,boolean) +pgr_trspvia_withpoints(text,text,text,anyarray,boolean,boolean,boolean,character,boolean) _pgr_trsp_withpoints(text,text,text,anyarray,anyarray,boolean,character,boolean) pgr_trsp_withpoints(text,text,text,anyarray,anyarray,boolean,character,boolean) pgr_trsp_withpoints(text,text,text,anyarray,bigint,boolean,character,boolean) diff --git a/sql/trsp/CMakeLists.txt b/sql/trsp/CMakeLists.txt index d9ffcb213db..e66117c87a9 100644 --- a/sql/trsp/CMakeLists.txt +++ b/sql/trsp/CMakeLists.txt @@ -15,6 +15,9 @@ SET(LOCAL_FILES trspVia.sql _trspVia.sql + + trspVia_withPoints.sql + _trspVia_withPoints.sql ) foreach (f ${LOCAL_FILES}) diff --git a/sql/trsp/_trspVia_withPoints.sql b/sql/trsp/_trspVia_withPoints.sql new file mode 100644 index 00000000000..bac44cf89d8 --- /dev/null +++ b/sql/trsp/_trspVia_withPoints.sql @@ -0,0 +1,56 @@ +/*PGR-GNU***************************************************************** +File: _trspVia_withPoints.sql + +Function's developer: +Copyright (c) 2022 Celia Virginia Vergara Castillo + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +--v3.0 +CREATE FUNCTION _pgr_trspVia_withPoints( + TEXT, -- edges + TEXT, -- restrictions + TEXT, -- points + ANYARRAY, -- via + -- via parameters + BOOLEAN, -- directed + BOOLEAN, -- strict + BOOLEAN, -- U_turn_on_edge + -- withPoints parameters + CHAR, -- driving_side + BOOLEAN, -- details + + OUT seq INTEGER, + OUT path_id INTEGER, + OUT path_seq INTEGER, + OUT start_vid BIGINT, + OUT end_vid BIGINT, + OUT node BIGINT, + OUT edge BIGINT, + OUT cost FLOAT, + OUT agg_cost FLOAT, + OUT route_agg_cost FLOAT) +RETURNS SETOF RECORD AS +'MODULE_PATHNAME' +LANGUAGE c VOLATILE STRICT; + +-- COMMENTS + +COMMENT ON FUNCTION _pgr_trspVia_withPoints(TEXT, TEXT, TEXT, ANYARRAY, BOOLEAN, BOOLEAN, BOOLEAN, CHAR, BOOLEAN) +IS 'pgRouting internal function'; diff --git a/sql/trsp/trspVia_withPoints.sql b/sql/trsp/trspVia_withPoints.sql new file mode 100644 index 00000000000..cece617129b --- /dev/null +++ b/sql/trsp/trspVia_withPoints.sql @@ -0,0 +1,83 @@ +/*PGR-GNU***************************************************************** +File: trspVia_withPoints.sql + +Function's developer: +Copyright (c) 2022 Celia Virginia Vergara Castillo + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +--v3.4 +CREATE FUNCTION pgr_trspVia_withPoints( + TEXT, -- edges sql + TEXT, -- restrictions sql + TEXT, -- points + ANYARRAY, -- via vids + + directed BOOLEAN DEFAULT true, + + -- via parameters + strict BOOLEAN DEFAULT false, + U_turn_on_edge BOOLEAN DEFAULT true, + + -- withPoints parameters + driving_side CHAR DEFAULT 'r', -- 'r'/'l' + details BOOLEAN DEFAULT false, + + OUT seq INTEGER, + OUT path_id INTEGER, + OUT path_seq INTEGER, + OUT start_vid BIGINT, + OUT end_vid BIGINT, + OUT node BIGINT, + OUT edge BIGINT, + OUT cost FLOAT, + OUT agg_cost FLOAT, + OUT route_agg_cost FLOAT) +RETURNS SETOF RECORD AS +$BODY$ +SELECT * +FROM _pgr_trspVia_withPoints( + _pgr_get_statement($1), + _pgr_get_statement($2), + _pgr_get_statement($3), + $4, $5, $6, $7, $8, $9); +$BODY$ +LANGUAGE SQL VOLATILE STRICT +COST 100 +ROWS 1000; + +-- COMMENTS + +COMMENT ON FUNCTION pgr_trspVia_withPoints(TEXT, TEXT, TEXT, ANYARRAY, BOOLEAN, BOOLEAN, BOOLEAN, CHAR, BOOLEAN) +IS 'pgr_trspVia_withPoints +- PROPOSED +- Parameters: + - Edges SQL with columns: id, source, target, cost [,reverse_cost] + - Restrictions SQL with columns: cost, path + - Points SQL with columns: [pid], edge_id, fraction [,side] + - ARRAY[via vertices identifiers] +- Optional Parameters + - directed := true + - strict := false + - U_turn_on_edge := true + - driving_side := ''r'' + - details := ''false'' +- Documentation: + - ${PROJECT_DOC_LINK}/pgr_trspVia_withPoints.html +'; From b65bd99d32a3cbc266c3c7418a64223b58f13e03 Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 10:32:42 -0600 Subject: [PATCH 04/14] [sql][trspViaEdges] deprecating function --- sql/trsp/pgr_trspViaEdges.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/trsp/pgr_trspViaEdges.sql b/sql/trsp/pgr_trspViaEdges.sql index 95d4503de35..8ef9dbc8f60 100644 --- a/sql/trsp/pgr_trspViaEdges.sql +++ b/sql/trsp/pgr_trspViaEdges.sql @@ -69,6 +69,7 @@ declare f float; begin + RAISE WARNING 'pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated'; SELECT 0::INTEGER AS seq, NULL::INTEGER AS id1, NULL::INTEGER AS id2, NULL::INTEGER AS id3, NULL::FLOAT AS cost INTO lrr; has_reverse =_pgr_parameter_check('dijkstra', sql, false); edges_sql := sql; @@ -174,7 +175,7 @@ rows 1000; COMMENT ON FUNCTION pgr_trspViaEdges(TEXT, INTEGER[], FLOAT[], BOOLEAN, BOOLEAN, TEXT) IS 'pgr_trspViaEdges -- PROTOTYPE +- DEPRECATED - Parameters - edges SQL with columns: id, source, target, cost [,reverse_cost] - ARRAY[Via edge identifiers] From d89dd154562e0c7c84dafe4d3e7238ec7931a700 Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 10:20:24 -0600 Subject: [PATCH 05/14] [src][trspwithPoints] fixing a bug about detail elimination --- include/cpp_common/basePath_SSEC.hpp | 2 + include/trsp/pgr_trspHandler.h | 18 +++--- src/common/basePath_SSEC.cpp | 9 +++ src/trsp/pgr_trspHandler.cpp | 91 ++++++++++++++++++++-------- src/trsp/trsp_withPoints.c | 3 + src/trsp/trsp_withPoints_driver.cpp | 9 +++ 6 files changed, 100 insertions(+), 32 deletions(-) diff --git a/include/cpp_common/basePath_SSEC.hpp b/include/cpp_common/basePath_SSEC.hpp index 08260cc5398..e7dd41b3071 100644 --- a/include/cpp_common/basePath_SSEC.hpp +++ b/include/cpp_common/basePath_SSEC.hpp @@ -37,6 +37,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include #include #include +#include #include "c_types/path_rt.h" #include "cpp_common/path_t.h" @@ -77,6 +78,7 @@ class Path { const Path_t& operator[](size_t i) const {return path[i];} Path_t& operator[](size_t i) {return path[i];} Path& renumber_vertices(int64_t value); + Path& renumber_vertices(const std::map&); pthIt begin() {return path.begin();} pthIt end() {return path.end();} diff --git a/include/trsp/pgr_trspHandler.h b/include/trsp/pgr_trspHandler.h index 47e67c28fe9..994b8c2d927 100644 --- a/include/trsp/pgr_trspHandler.h +++ b/include/trsp/pgr_trspHandler.h @@ -43,6 +43,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include "cpp_common/basePath_SSEC.hpp" #include "trsp/edgeInfo.h" #include "cpp_common/rule.h" +#include "cpp_common/pgr_messages.h" namespace pgrouting { namespace trsp { @@ -50,7 +51,7 @@ namespace trsp { -class Pgr_trspHandler { +class Pgr_trspHandler : public pgrouting::Pgr_messages { /** * Used in the priority queue */ @@ -175,10 +176,8 @@ class Pgr_trspHandler { double construct_path(int64_t ed_id, Position pos); - - int64_t renumber_edges( - Edge_t *edges, - const size_t edge_count) const; + void renumber_edges(Edge_t*, const size_t); + void renumber_edges(Edge_t*, const size_t, std::vector&); void add_to_que( double cost, @@ -195,7 +194,8 @@ class Pgr_trspHandler { /** * Used only to veryfy that there are no reppetitions inserted - * the way it orks, repeating edges id is not allowed + * the way it works, repeating edges id is not allowed + * TODO when using points edges id are repeated */ std::map m_mapEdgeId2Index; @@ -204,6 +204,10 @@ class Pgr_trspHandler { */ std::map> m_adjacency; + /* id -> idx */ + std::map m_id_to_idx; + /* idx -> id */ + std::map m_idx_to_id; int64_t m_start_vertex; int64_t m_end_vertex; @@ -213,8 +217,6 @@ class Pgr_trspHandler { */ int64_t current_node; - int64_t m_min_id; - Path m_path; std::vector m_parent; diff --git a/src/common/basePath_SSEC.cpp b/src/common/basePath_SSEC.cpp index 9bb754e4c1f..4b077900e61 100644 --- a/src/common/basePath_SSEC.cpp +++ b/src/common/basePath_SSEC.cpp @@ -30,10 +30,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include #include #include +#include #include "c_types/path_rt.h" #include "cpp_common/pgr_assert.h" +Path& Path::renumber_vertices(const std::map& idx_to_id) { + for (auto &r : path) { + r.node = idx_to_id.at(r.node); + } + m_start_id = idx_to_id.at(m_start_id); + m_end_id = idx_to_id.at(m_end_id); + return *this; +} Path& Path::renumber_vertices(int64_t value) { for (auto &r : path) { diff --git a/src/trsp/pgr_trspHandler.cpp b/src/trsp/pgr_trspHandler.cpp index c8cff05d9d5..478ad4c80da 100644 --- a/src/trsp/pgr_trspHandler.cpp +++ b/src/trsp/pgr_trspHandler.cpp @@ -52,7 +52,10 @@ Pgr_trspHandler::Pgr_trspHandler( m_ruleTable() { initialize_restrictions(ruleList); - m_min_id = renumber_edges(edges, edge_count); + renumber_edges(edges, edge_count); + for (const auto& id : m_id_to_idx) { + m_idx_to_id[id.second] = id.first; + } construct_graph( edges, @@ -69,42 +72,75 @@ Pgr_trspHandler::Pgr_trspHandler( m_ruleTable() { initialize_restrictions(ruleList); - m_min_id = renumber_edges(edges, edge_count); + auto point_edges = new_edges; + renumber_edges(edges, edge_count, point_edges); + + for (const auto& id : m_id_to_idx) { + m_idx_to_id[id.second] = id.first; + } construct_graph( edges, edge_count, directed); add_point_edges( - new_edges, + point_edges, directed); } // ------------------------------------------------------------------------- -int64_t +void Pgr_trspHandler::renumber_edges( Edge_t *edges, - size_t total_edges) const { - int64_t v_min_id = INT64_MAX; - size_t z; - for (z = 0; z < total_edges; z++) { - if (edges[z].source < v_min_id) - v_min_id = edges[z].source; - - if (edges[z].target < v_min_id) - v_min_id = edges[z].target; + size_t total_edges) { + int64_t idx(0); + for (size_t i = 0; i < total_edges; ++i) { + if (m_id_to_idx.find(edges[i].source) == m_id_to_idx.end()) { + m_id_to_idx[edges[i].source] = idx; + ++idx; } - - for (z = 0; z < total_edges; z++) { - edges[z].source -= v_min_id; - edges[z].target -= v_min_id; + if (m_id_to_idx.find(edges[i].target) == m_id_to_idx.end()) { + m_id_to_idx[edges[i].target] = idx; + ++idx; } - - return v_min_id; + edges[i].source = m_id_to_idx.at(edges[i].source); + edges[i].target = m_id_to_idx.at(edges[i].target); + } } +void +Pgr_trspHandler::renumber_edges( + Edge_t *edges, + size_t total_edges, + std::vector& new_edges) { + int64_t idx(0); + for (size_t i = 0; i < total_edges; ++i) { + if (m_id_to_idx.find(edges[i].source) == m_id_to_idx.end()) { + m_id_to_idx[edges[i].source] = idx; + ++idx; + } + if (m_id_to_idx.find(edges[i].target) == m_id_to_idx.end()) { + m_id_to_idx[edges[i].target] = idx; + ++idx; + } + edges[i].source = m_id_to_idx.at(edges[i].source); + edges[i].target = m_id_to_idx.at(edges[i].target); + } + for (auto &e : new_edges) { + if (m_id_to_idx.find(e.source) == m_id_to_idx.end()) { + m_id_to_idx[e.source] = idx; + ++idx; + } + if (m_id_to_idx.find(e.target) == m_id_to_idx.end()) { + m_id_to_idx[e.target] = idx; + ++idx; + } + e.source = m_id_to_idx.at(e.source); + e.target = m_id_to_idx.at(e.target); + } +} @@ -285,9 +321,13 @@ Pgr_trspHandler::process( const int64_t start_vertex, const int64_t end_vertex) { clear(); + pgassert(m_id_to_idx.find(start_vertex) != m_id_to_idx.end()); + pgassert(m_id_to_idx.find(end_vertex) != m_id_to_idx.end()); - m_start_vertex = start_vertex - m_min_id; - m_end_vertex = end_vertex - m_min_id; + m_start_vertex = m_id_to_idx.at(start_vertex); + m_end_vertex = m_id_to_idx.at(end_vertex); + pgassert(m_idx_to_id.at(m_start_vertex) == start_vertex); + pgassert(m_idx_to_id.at(m_end_vertex) == end_vertex); Path tmp(m_start_vertex, m_end_vertex); m_path = tmp; @@ -442,7 +482,7 @@ Pgr_trspHandler::process_trsp( pgassert(m_path.start_id() == m_start_vertex); if (current_node != m_end_vertex) { Path result(m_start_vertex, m_end_vertex); - return result.renumber_vertices(m_min_id);; + return result.renumber_vertices(m_idx_to_id);; } pgassert(m_path.start_id() == m_start_vertex); @@ -460,7 +500,7 @@ Pgr_trspHandler::process_trsp( m_path.push_back(pelement); m_path.Path::recalculate_agg_cost(); - return m_path.renumber_vertices(m_min_id); + return m_path.renumber_vertices(m_idx_to_id); } @@ -550,6 +590,7 @@ bool Pgr_trspHandler::addEdge(Edge_t edgeIn, bool directed) { } } +#if 0 /* * The edge was already inserted * @@ -561,7 +602,7 @@ bool Pgr_trspHandler::addEdge(Edge_t edgeIn, bool directed) { if (m_mapEdgeId2Index.find(edgeIn.id) != m_mapEdgeId2Index.end()) { return false; } - +#endif /* * the index of this new edge in the edges container is @@ -569,11 +610,13 @@ bool Pgr_trspHandler::addEdge(Edge_t edgeIn, bool directed) { */ EdgeInfo edge(edgeIn, m_edges.size()); +#if 0 // Adding edge to the list m_mapEdgeId2Index.insert( std::make_pair( edge.edgeID(), m_edges.size())); +#endif m_edges.push_back(edge); diff --git a/src/trsp/trsp_withPoints.c b/src/trsp/trsp_withPoints.c index 1f20996d5e3..baad777ead1 100644 --- a/src/trsp/trsp_withPoints.c +++ b/src/trsp/trsp_withPoints.c @@ -70,6 +70,9 @@ process( Path_rt **result_tuples, size_t *result_count) { driving_side[0] = estimate_drivingSide(driving_side[0]); + if (driving_side[0] != 'r' && driving_side[0] != 'l') { + driving_side[0] = 'l'; + } pgr_SPI_connect(); diff --git a/src/trsp/trsp_withPoints_driver.cpp b/src/trsp/trsp_withPoints_driver.cpp index 1a845ffc310..e6dfd47bc25 100644 --- a/src/trsp/trsp_withPoints_driver.cpp +++ b/src/trsp/trsp_withPoints_driver.cpp @@ -129,6 +129,7 @@ do_trsp_withPoints( true, driving_side, directed); + log << pg_graph.get_log(); if (pg_graph.has_error()) { log << pg_graph.get_log(); @@ -165,6 +166,10 @@ do_trsp_withPoints( } post_process_trsp(paths); + if (!details) { + for (auto &path : paths) path = pg_graph.eliminate_details(path); + } + size_t count(0); count = count_tuples(paths); @@ -216,6 +221,10 @@ do_trsp_withPoints( paths.insert(paths.end(), new_paths.begin(), new_paths.end()); } post_process_trsp(paths); + if (!details) { + for (auto &path : paths) path = pg_graph.eliminate_details(path); + } + count = count_tuples(paths); From faa2b1a49cc3cde0c9a29702588a20bd6c99b366 Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 10:30:16 -0600 Subject: [PATCH 06/14] [sql][trspwithPoints] fixing a bug about detail elimination --- sql/trsp/trsp_withPoints.sql | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sql/trsp/trsp_withPoints.sql b/sql/trsp/trsp_withPoints.sql index 78250bcf893..8649ca00fbb 100644 --- a/sql/trsp/trsp_withPoints.sql +++ b/sql/trsp/trsp_withPoints.sql @@ -34,7 +34,7 @@ CREATE FUNCTION pgr_trsp_withPoints( BIGINT, -- end_vid directed BOOLEAN DEFAULT true, - driving_side CHAR DEFAULT 'b', -- 'r'/'l'/'b' + driving_side CHAR DEFAULT 'r', -- 'r'/'l' details BOOLEAN DEFAULT false, OUT seq INTEGER, @@ -69,7 +69,7 @@ CREATE FUNCTION pgr_trsp_withPoints( ANYARRAY, -- end_vids directed BOOLEAN DEFAULT true, - driving_side CHAR DEFAULT 'b', -- 'r'/'l'/'b' + driving_side CHAR DEFAULT 'r', -- 'r'/'l' details BOOLEAN DEFAULT false, OUT seq INTEGER, @@ -103,7 +103,7 @@ CREATE FUNCTION pgr_trsp_withPoints( BIGINT, -- end_vid directed BOOLEAN DEFAULT true, - driving_side CHAR DEFAULT 'b', -- 'r'/'l'/'b' + driving_side CHAR DEFAULT 'r', -- 'r'/'l' details BOOLEAN DEFAULT false, OUT seq INTEGER, @@ -137,7 +137,7 @@ CREATE FUNCTION pgr_trsp_withPoints( ANYARRAY, -- end_vids directed BOOLEAN DEFAULT true, - driving_side CHAR DEFAULT 'b', -- 'r'/'l'/'b' + driving_side CHAR DEFAULT 'r', -- 'r'/'l' details BOOLEAN DEFAULT false, OUT seq INTEGER, @@ -171,7 +171,7 @@ CREATE FUNCTION pgr_trsp_withPoints( TEXT, -- combinations SQL directed BOOLEAN DEFAULT true, - driving_side CHAR DEFAULT 'b', -- 'r'/'l'/'b' + driving_side CHAR DEFAULT 'r', -- 'r'/'l' details BOOLEAN DEFAULT false, OUT seq INTEGER, @@ -208,7 +208,7 @@ IS 'pgr_trsp_withPoints (One to One) - Destination vertex/point identifier - Optional Parameters: - directed := ''true'' - - driving_side := ''b'' + - driving_side := ''r'' - details := ''false'' - Documentation: - ${PROJECT_DOC_LINK}/pgr_trsp_withPoints.html @@ -226,7 +226,7 @@ IS 'pgr_trsp_withPoints(One to Many) - Destinations ARRAY[vertices/Points identifier] - Optional Parameters: - directed := ''true'' - - driving_side := ''b'' + - driving_side := ''r'' - details := ''false'' - Documentation: - ${PROJECT_DOC_LINK}/pgr_trsp_withPoints.html @@ -243,7 +243,7 @@ IS 'pgr_trsp_withPoints(Many to One) - Destination vertex/point identifier - Optional Parameters: - directed := ''true'' - - driving_side := ''b'' + - driving_side := ''r'' - details := ''false'' - Documentation: - ${PROJECT_DOC_LINK}/pgr_trsp_withPoints.html @@ -260,7 +260,7 @@ IS 'pgr_trsp_withPoints(Many to Many) - Destinations ARRAY[vertices/Points identifier] - Optional Parameters: - directed := ''true'' - - driving_side := ''b'' + - driving_side := ''r'' - details := ''false'' - Documentation: - ${PROJECT_DOC_LINK}/pgr_trsp_withPoints.html @@ -275,7 +275,7 @@ IS 'pgr_trsp_withPoints(Combinations) - Combinations SQL with columns: source, target - Optional Parameters: - directed := ''true'' - - driving_side := ''b'' + - driving_side := ''r'' - details := ''false'' - Documentation: - ${PROJECT_DOC_LINK}/pgr_trsp_withPoints.html From 1ccb10c7daed9bfa71ba2e7556dfbab604390d9f Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 10:56:03 -0600 Subject: [PATCH 07/14] [doc][trspVia_withPoints] adding documentation --- doc/images/CMakeLists.txt | 1 + doc/images/restrictions.png | Bin 0 -> 72114 bytes doc/trsp/CMakeLists.txt | 1 + doc/trsp/pgr_trspVia_withPoints.rst | 312 ++++++++++++++++++++++++++++ 4 files changed, 314 insertions(+) create mode 100644 doc/images/restrictions.png create mode 100644 doc/trsp/pgr_trspVia_withPoints.rst diff --git a/doc/images/CMakeLists.txt b/doc/images/CMakeLists.txt index 67bd9cd206d..0d9b4477b7d 100644 --- a/doc/images/CMakeLists.txt +++ b/doc/images/CMakeLists.txt @@ -9,6 +9,7 @@ SET(LOCAL_FILES test1.png trsp-test-image.png with_restrictions.png + restrictions.png ) foreach (f ${LOCAL_FILES}) diff --git a/doc/images/restrictions.png b/doc/images/restrictions.png new file mode 100644 index 0000000000000000000000000000000000000000..652ad82cf613857d16cdcf7f46b673183968c391 GIT binary patch literal 72114 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%0Ai>YY194g8)~?23U|9fid;7S)jqbNi0Qt6tvF&P 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 literal 0 HcmV?d00001 diff --git a/doc/trsp/CMakeLists.txt b/doc/trsp/CMakeLists.txt index dfeef93426a..fb73962201b 100644 --- a/doc/trsp/CMakeLists.txt +++ b/doc/trsp/CMakeLists.txt @@ -3,6 +3,7 @@ SET(LOCAL_FILES TRSP-family.rst pgr_trsp.rst pgr_trspVia.rst + pgr_trspVia_withPoints.rst pgr_trsp_withPoints.rst pgr_turnRestrictedPath.rst ) diff --git a/doc/trsp/pgr_trspVia_withPoints.rst b/doc/trsp/pgr_trspVia_withPoints.rst new file mode 100644 index 00000000000..4ca734aa0d7 --- /dev/null +++ b/doc/trsp/pgr_trspVia_withPoints.rst @@ -0,0 +1,312 @@ +.. + **************************************************************************** + pgRouting Manual + Copyright(c) pgRouting Contributors + + This documentation is licensed under a Creative Commons Attribution-Share + Alike 3.0 License: https://creativecommons.org/licenses/by-sa/3.0/ + **************************************************************************** + +| + +* **Supported versions:** + `Latest `__ + (`3.4 `__) + +``pgr_trspVia_withPoints`` - Proposed +=============================================================================== + +``pgr_trspVia_withPoints`` Via vertices/points routing with restrictions. + +.. include:: proposed.rst + :start-after: stable-begin-warning + :end-before: stable-end-warning + +.. figure:: images/boost-inside.jpeg + :target: https://www.boost.org/libs/graph/doc/table_of_contents.html + + Boost Graph Inside + +.. rubric:: Availability + +* Version 3.4.0 + + * New **proposed** function ``pgr_trspVia_withPoints`` (`One Via`_) + +| + +Description +------------------------------------------------------------------------------- + +Given a graph, a set of restriction on the graph edges, a set of points on the +graphs edges and a list of vertices, this function is equivalent to finding the +shortest path between :math:`vertex_i` and :math:`vertex_{i+1}` (where +:math:`vertex` can be a vertex or a point on the graph) for all :math:`i < +size\_of(via\;vertices)` trying not to use restricted paths. + +:Route: is a sequence of paths +:Path: is a section of the route. + +The general algorithm is as follows: + +* Build the Graph with the new points. + + * The points identifiers will be converted to negative values. + * The vertices identifiers will remain positive. + +* Execute a :doc:`pgr_withPointsVia`. +* For the set of paths of the solution that pass through a restriction then + + * Execute the **TRSP** algorithm with restrictions for the path. + * **NOTE** when this is done, ``U_turn_on_edge`` flag is ignored. + +.. Note:: Do not use negative values on identifiers of the inner queries. + +| + +Signatures +------------------------------------------------------------------------------- + +.. rubric:: Summary + +.. index:: + single: trspVia - Proposed on v3.4 + +.. parsed-literal:: + + pgr_trspVia_withPoints(`Edges SQL`_, `Restrictions SQL`_, `Points SQL`_, **via vertices** + [, directed] [, strict] [, U_turn_on_edge]) -- Proposed on v3.4 + RETURNS SET OF (seq, path_pid, path_seq, start_vid, end_vid, + node, edge, cost, agg_cost, route_agg_cost) + OR EMPTY SET + +| + +One Via +............................................................................... + +.. parsed-literal:: + + pgr_trspVia_withPoints(`Edges SQL`_, `Restrictions SQL`_, `Points SQL`_, **via vertices** + [, directed] [, strict] [, U_turn_on_edge]) -- Proposed on v3.4 + RETURNS SET OF (seq, path_pid, path_seq, start_vid, end_vid, + node, edge, cost, agg_cost, route_agg_cost) + OR EMPTY SET + +:Example: Find the route that visits the vertices :math:`\{ 1, 7, 10\}` in that + order on an **directed** graph. + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q0 + :end-before: -- q1 + +| + +Parameters +------------------------------------------------------------------------------- + +.. list-table:: + :width: 81 + :widths: 14 20 7 40 + :header-rows: 1 + + * - Parameter + - Type + - Default + - Description + * - `Edges SQL`_ + - ``TEXT`` + - + - SQL query as described. + * - `Restrictions SQL`_ + - ``TEXT`` + - + - SQL query as described. + * - `Points SQL`_ + - ``TEXT`` + - + - SQL query as described. + * - **via vertices** + - ``ARRAY[`` **ANY-INTEGER** ``]`` + - + - Array of ordered vertices identifiers that are going to be visited. + + * When positive it is considered a vertex identifier + * When negative it is considered a point identifier + * - ``directed`` + - ``BOOLEAN`` + - ``true`` + - - When ``true`` Graph is considered `Directed` + - When ``false`` the graph is considered as Undirected. + +| + +Via optional parameters +............................................................................... + +.. include:: via-category.rst + :start-after: via_opt_parameters_start + :end-before: via_opt_parameters_end + +| + +With points optional parameters +............................................................................... + +.. include:: pgr_trsp_withPoints.rst + :start-after: withPoints_parameters_start + :end-before: withPoints_parameters_end + +| + +Inner query +------------------------------------------------------------------------------- + +| + +Edges SQL +............................................................................... + +.. include:: pgRouting-concepts.rst + :start-after: basic_edges_sql_start + :end-before: basic_edges_sql_end + +| + +Restrictions SQL +............................................................................... + +.. include:: TRSP-family.rst + :start-after: restrictions_columns_start + :end-before: restrictions_columns_end + +| + +Points SQL +............................................................................... + +.. include:: pgRouting-concepts.rst + :start-after: points_sql_start + :end-before: points_sql_end + +| + +Return Columns +------------------------------------------------------------------------------- + +.. include:: via-category.rst + :start-after: result columns start + :end-before: result columns end + +| + +Additional Examples +------------------------------------------------------------------------------- + +:Example 1: Find the route that visits the vertices :math:`\{1, 5, 7, 10, 4\}` + in that order + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q1 + :end-before: -- q2 + +:Example 2: What's the aggregate cost of the third path? + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q2 + :end-before: -- q3 + +:Example 3: What's the route's aggregate cost of the route at the end of the + third path? + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q3 + :end-before: -- q4 + +:Example 4: How are the nodes visited in the route? + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q4 + :end-before: -- q5 + +:Example 5: What are the aggregate costs of the route when the visited vertices + are reached? + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q5 + :end-before: -- q6 + +:Example 6: Show the route's seq and aggregate cost and a status of "passes in + front" or "visits" + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q6 + :end-before: -- q7 + +:Example 7: Simulation of how algorithm works + +The algorithm performs a :doc:`pgr_withPointsVia` + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q7 + :end-before: -- q8 + +Detects which of the paths pass through a restriction in this case is for the +``path_id = 1`` from ``-6`` to ``4`` because the path :math:`9 \rightarrow 16` +is restricted. + +Executes the :ref:`TRSP-family:TRSP algorithm` for the conflicting paths. + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q8 + :end-before: -- q9 + +From the :doc:`pgr_dijkstraVia` result it removes the conflicting paths and +builds the solution with the results of the :doc:`pgr_trsp` algorithm: + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q9 + :end-before: -- q10 + +Getting the same result as ``pgr_trspVia_withPoints``: + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q10 + :end-before: -- q11 + +:Example 8: Sometimes ``U_turn_on_edge`` flag is ignored when is set to + ``false``. + +The first step, doing a :doc:`pgr_dijkstraVia` does consider not making a U turn +on the same edge. But the path :math:`9 \rightarrow 16` (Rows 4 and 5) is +restricted and the result is using it. + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q11 + :end-before: -- q12 + +When executing the :doc:`pgr_trsp_withPoints` algorithm for the conflicting +path, there is no ``U_turn_on_edge`` flag. + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q12 + :end-before: -- q13 + +Therefore the result ignores the ``U_turn_on_edge`` flag when set to ``false``. + +.. literalinclude:: trspVia_withPoints.queries + :start-after: -- q13 + :end-before: -- q14 + +See Also +------------------------------------------------------------------------------- + +* :doc:`TRSP-family` +* :doc:`via-category` +* :doc:`sampledata` network. + +.. rubric:: Indices and tables + +* :ref:`genindex` +* :ref:`search` + From 71ef6e8eb14a2a2721945c0cf576cd1a7f584ca8 Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 10:36:44 -0600 Subject: [PATCH 08/14] [pgtap][trspVia_withPoints] adding tap tests --- pgtap/trsp/trspViaEdges/no_crash_test.pg | 1 + .../no_restrictions_directed.pg | 11 +++ .../no_restrictions_undirected.pg | 10 +++ pgtap/trsp/trspVia_withPoints/edge_cases.pg | 66 ++++++++++++++++ pgtap/trsp/trspVia_withPoints/inner_query.pg | 30 +++++++ .../trsp/trspVia_withPoints/no_crash_test.pg | 65 ++++++++++++++++ pgtap/trsp/trspVia_withPoints/types_check.pg | 8 ++ tools/testers/via_compare.sql | 78 +++++++++++++++++++ 8 files changed, 269 insertions(+) create mode 100644 pgtap/trsp/trspVia_withPoints/compare_dijkstraVia/no_restrictions_directed.pg create mode 100644 pgtap/trsp/trspVia_withPoints/compare_dijkstraVia/no_restrictions_undirected.pg create mode 100644 pgtap/trsp/trspVia_withPoints/edge_cases.pg create mode 100644 pgtap/trsp/trspVia_withPoints/inner_query.pg create mode 100644 pgtap/trsp/trspVia_withPoints/no_crash_test.pg create mode 100644 pgtap/trsp/trspVia_withPoints/types_check.pg diff --git a/pgtap/trsp/trspViaEdges/no_crash_test.pg b/pgtap/trsp/trspViaEdges/no_crash_test.pg index 656d51365a2..c11a12704f6 100644 --- a/pgtap/trsp/trspViaEdges/no_crash_test.pg +++ b/pgtap/trsp/trspViaEdges/no_crash_test.pg @@ -2,6 +2,7 @@ BEGIN; UPDATE edge_table SET cost = sign(cost), reverse_cost = sign(reverse_cost); SELECT CASE WHEN min_version('4.0.0') THEN plan(22) ELSE plan(21) END; +SET client_min_messages TO ERROR; PREPARE edges AS SELECT id, source, target, cost, reverse_cost FROM edge_table; diff --git a/pgtap/trsp/trspVia_withPoints/compare_dijkstraVia/no_restrictions_directed.pg b/pgtap/trsp/trspVia_withPoints/compare_dijkstraVia/no_restrictions_directed.pg new file mode 100644 index 00000000000..080458fb71d --- /dev/null +++ b/pgtap/trsp/trspVia_withPoints/compare_dijkstraVia/no_restrictions_directed.pg @@ -0,0 +1,11 @@ +BEGIN; + +SELECT CASE WHEN min_version('3.4.0') THEN plan(2048) ELSE plan(1) END; +SET extra_float_digits = -3; +UPDATE edge_table SET cost = sign(cost) + 0.001 * id * id, reverse_cost = sign(reverse_cost) + 0.001 * id * id; + +SELECT trspVia_withPoints_VS_withPointsVia(16, true); + +SELECT * FROM finish(); +ROLLBACK; + diff --git a/pgtap/trsp/trspVia_withPoints/compare_dijkstraVia/no_restrictions_undirected.pg b/pgtap/trsp/trspVia_withPoints/compare_dijkstraVia/no_restrictions_undirected.pg new file mode 100644 index 00000000000..4976b07bb96 --- /dev/null +++ b/pgtap/trsp/trspVia_withPoints/compare_dijkstraVia/no_restrictions_undirected.pg @@ -0,0 +1,10 @@ +BEGIN; + +SELECT CASE WHEN min_version('3.4.0') THEN plan(2048) ELSE plan(1) END; +SET extra_float_digits = -3; +UPDATE edge_table SET cost = sign(cost) + 0.001 * id * id, reverse_cost = sign(reverse_cost) + 0.001 * id * id; + +SELECT trspVia_withPoints_VS_withPointsVia(16, false); + +SELECT * FROM finish(); +ROLLBACK; diff --git a/pgtap/trsp/trspVia_withPoints/edge_cases.pg b/pgtap/trsp/trspVia_withPoints/edge_cases.pg new file mode 100644 index 00000000000..f00413387a7 --- /dev/null +++ b/pgtap/trsp/trspVia_withPoints/edge_cases.pg @@ -0,0 +1,66 @@ +BEGIN; + +UPDATE edge_table SET cost = sign(cost), reverse_cost = sign(reverse_cost); +SELECT CASE WHEN min_version('3.4.0') THEN plan(5) ELSE plan(1) END; +SET client_min_messages TO ERROR; + +CREATE OR REPLACE FUNCTION edge_cases() +RETURNS SETOF TEXT AS +$BODY$ +DECLARE + restriction TEXT; + unrelated_restriction TEXT; + empty_restriction TEXT; +BEGIN + + IF NOT min_version('3.4.0') THEN + RETURN QUERY SELECT skip(1, 'New function on 3.4'); + RETURN; + END IF; + + RETURN QUERY SELECT lives_ok($q$ + SELECT * FROM pgr_trspVia_withPoints( + $$SELECT * FROM edge_table$$, + $$SELECT 100::FLOAT AS cost, ARRAY[]::BIGINT[] AS path$$, + $$SELECT * FROM pointsOfInterest$$, + ARRAY[2,3])$q$, $q$empty restriction path lives$q$); + + RETURN QUERY SELECT isnt_empty($q$ + SELECT * FROM pgr_trspVia_withPoints( + $$SELECT * FROM edge_table$$, + $$SELECT 100::FLOAT AS cost, ARRAY[]::BIGINT[] AS path$$, + $$SELECT * FROM pointsOfInterest$$, + ARRAY[2,3])$q$, $q$empty restriction path gives non empty result$q$); + + RETURN QUERY SELECT lives_ok($q$ + SELECT * FROM pgr_trspVia_withPoints( + $$SELECT * FROM edge_table$$, + $$SELECT 100::FLOAT AS cost, NULL::BIGINT[] AS path$$, + $$SELECT * FROM pointsOfInterest$$, + ARRAY[2,3])$q$, $q$null restriction path lives$q$); + + RETURN QUERY SELECT isnt_empty($q$ + SELECT * FROM pgr_trspVia_withPoints( + $$SELECT * FROM edge_table$$, + $$SELECT 100::FLOAT AS cost, NULL::BIGINT[] AS path$$, + $$SELECT * FROM pointsOfInterest$$, + ARRAY[2,3])$q$, $q$null restriction path gives non empty result$q$); + + RETURN QUERY SELECT throws_ok($q$ + SELECT * FROM pgr_trspVia_withPoints( + $$SELECT * FROM edge_table$$, + $$SELECT 100::FLOAT AS cost, ARRAY[NULL]::BIGINT[] AS path$$, + $$SELECT * FROM pointsOfInterest$$, + ARRAY[2,3])$q$, + $$XX000$$,$$NULL value found in Array!$$, +$q$array containing a null path throws$q$); + +END; +$BODY$ +LANGUAGE plpgsql; + +SELECT edge_cases(); + + +SELECT finish(); +ROLLBACK; diff --git a/pgtap/trsp/trspVia_withPoints/inner_query.pg b/pgtap/trsp/trspVia_withPoints/inner_query.pg new file mode 100644 index 00000000000..6a77166ce58 --- /dev/null +++ b/pgtap/trsp/trspVia_withPoints/inner_query.pg @@ -0,0 +1,30 @@ +BEGIN; + +UPDATE edge_table SET cost = sign(cost), reverse_cost = sign(reverse_cost); +SELECT CASE WHEN min_version('3.4.0') THEN plan(96) ELSE plan(1) END; + +CREATE OR REPLACE FUNCTION inner_query() +RETURNS SETOF TEXT AS +$BODY$ +BEGIN + IF NOT min_version('3.4.0') THEN + RETURN QUERY SELECT skip(1, 'Signature added on 3.4.0'); + RETURN; + END IF; + + -- ONE VIA + RETURN QUERY SELECT style_dijkstra( + 'pgr_trspVia_withPoints(', ', $$SELECT * from restrictions$$, $$SELECT * FROM pointsOfInterest$$, ARRAY[2,9,12])'); + RETURN QUERY SELECT innerquery_restrictions( + 'pgr_trspVia_withPoints($$SELECT * FROM edge_table$$,', ', $$SELECT * FROM pointsOfInterest$$, ARRAY[2,9,12])'); + RETURN QUERY SELECT innerquery_points( + 'pgr_trspVia_withPoints($$SELECT * FROM edge_table$$, $$SELECT * from restrictions$$,', ', ARRAY[2,9,12])'); + +END; +$BODY$ +LANGUAGE plpgsql; + +SELECT inner_query(); + +SELECT finish(); +ROLLBACK; diff --git a/pgtap/trsp/trspVia_withPoints/no_crash_test.pg b/pgtap/trsp/trspVia_withPoints/no_crash_test.pg new file mode 100644 index 00000000000..4590d68e4a2 --- /dev/null +++ b/pgtap/trsp/trspVia_withPoints/no_crash_test.pg @@ -0,0 +1,65 @@ +BEGIN; + +UPDATE edge_table SET cost = sign(cost), reverse_cost = sign(reverse_cost); +SELECT CASE WHEN min_version('3.4.0') THEN plan(23) ELSE plan(4) END; + +PREPARE edges AS +SELECT id, source, target, cost, reverse_cost FROM edge_table; + +PREPARE null_ret AS +SELECT id FROM edge_table_vertices_pgr WHERE id IN (-1); + +PREPARE null_ret_arr AS +SELECT array_agg(id) FROM edge_table_vertices_pgr WHERE id IN (-1); + +SELECT isnt_empty('edges', 'Should be not empty to tests be meaningful'); +SELECT is_empty('null_ret', 'Should be empty to tests be meaningful'); +SELECT set_eq('null_ret_arr', 'SELECT NULL::BIGINT[]', 'Should be empty to tests be meaningful'); + + +CREATE OR REPLACE FUNCTION test_function() +RETURNS SETOF TEXT AS +$BODY$ +DECLARE +params TEXT[]; +subs TEXT[]; +BEGIN + +IF NOT min_version('3.4.0') THEN + RETURN QUERY SELECT skip(1, 'Signature added on 3.4.0'); + RETURN; +END IF; + +-- ONE VIA +params = ARRAY[ +'$$SELECT id, source, target, cost, reverse_cost FROM edge_table$$', +'$$SELECT path, cost FROM restrictions$$', +'$$SELECT * FROM pointsofinterest$$', +'ARRAY[2,5]::BIGINT[]' +]::TEXT[]; + +subs = ARRAY[ +'NULL', +'NULL', +'NULL', +'(SELECT array_agg(id) FROM edge_table_vertices_pgr WHERE id IN (-1))' +]::TEXT[]; + +RETURN query SELECT * FROM no_crash_test('pgr_trspvia_withPoints', params, subs); + +subs = ARRAY[ +'NULL', +'NULL', +'NULL', +'NULL::BIGINT[]' +]::TEXT[]; +RETURN query SELECT * FROM no_crash_test('pgr_trspvia_withPoints', params, subs); + +END +$BODY$ +LANGUAGE plpgsql VOLATILE; + + +SELECT * FROM test_function(); + +ROLLBACK; diff --git a/pgtap/trsp/trspVia_withPoints/types_check.pg b/pgtap/trsp/trspVia_withPoints/types_check.pg new file mode 100644 index 00000000000..7338000c8f6 --- /dev/null +++ b/pgtap/trsp/trspVia_withPoints/types_check.pg @@ -0,0 +1,8 @@ +BEGIN; + +SELECT CASE WHEN min_version('3.4.0') THEN plan(5) ELSE plan(1) END; + +SELECT types_check_via('pgr_trspvia_withpoints'); + +SELECT * FROM finish(); +ROLLBACK; diff --git a/tools/testers/via_compare.sql b/tools/testers/via_compare.sql index e70e953a508..49108e13c2b 100644 --- a/tools/testers/via_compare.sql +++ b/tools/testers/via_compare.sql @@ -133,6 +133,84 @@ BEGIN RETURN query SELECT set_eq(withPoints_sql, dijkstraVia_sql, msg); + k := k + 1; + + END LOOP; + END LOOP; + END LOOP; + END LOOP; +END +$BODY$ +language plpgsql; + +CREATE OR REPLACE FUNCTION trspVia_withPoints_VS_withPointsVia(cant INTEGER default 18, flag boolean default true ) +RETURNS SETOF TEXT AS +$BODY$ +DECLARE +dijkstraVia_sql TEXT; +withPoints_sql TEXT; +with_reverse_cost TEXT; +no_reverse_cost TEXT; +the_points TEXT; +empty_restrictions TEXT; +k INTEGER; +directed TEXT; +msg TEXT; +msg_end TEXT; +all_found BOOLEAN; +allow_u BOOLEAN; +BEGIN + IF NOT min_version('3.4.0') THEN + RETURN QUERY SELECT skip(1, 'Signature added on 3.4.0'); + RETURN; + END IF; + + directed = 'Undirected'; + IF flag THEN directed = 'Directed'; END IF; + + k := 1; + with_reverse_cost = quote_literal('SELECT id, source, target, cost, reverse_cost from edge_table ORDER BY id'); + no_reverse_cost = quote_literal('SELECT id, source, target, cost from edge_table ORDER BY id'); + the_points = quote_literal('SELECT * FROM pointsOfInterest'); + empty_restrictions = quote_literal('SELECT * FROM restrictions WHERE id > 7'); + msg_end = ' '; + + FOR i IN 1 .. cant LOOP + FOR j IN 1 .. cant LOOP + FOR l IN 0 .. 1 LOOP + FOR m IN 0 .. 1 LOOP + all_found := (l=1); + allow_u := (m=1); + + dijkstraVia_sql := 'SELECT * FROM pgr_withPointsVia( ' + || with_reverse_cost || ',' + || the_points + || ', ARRAY[1, ' || i || ', ' || j || '], ' || flag || ',' || all_found || ',' || allow_u || ')'; + + withPoints_sql := 'SELECT * FROM pgr_trspVia_withPoints(' + || with_reverse_cost || ',' + || empty_restrictions || ',' + || the_points + || ', ARRAY[1, ' || i || ', ' || j || '], ' || flag || ',' || all_found || ',' || allow_u || ')'; + + msg := '-1- ' || directed || ', with reverse_cost: from 1 to ' || i || ' to ' || j || msg_end || withPoints_sql; + RETURN query SELECT set_eq(withPoints_sql, dijkstraVia_sql, msg); + + dijkstraVia_sql := 'SELECT * FROM pgr_withPointsVia( ' + || no_reverse_cost || ',' + || the_points + || ', ARRAY[1, ' || i || ', ' || j || '], ' || flag || ',' || all_found || ',' || allow_u || ')'; + + withPoints_sql := 'SELECT * FROM pgr_trspVia_withPoints(' + || no_reverse_cost || ',' + || empty_restrictions || ',' + || the_points + || ', ARRAY[1, ' || i || ', ' || j || '], ' || flag || ',' || all_found || ',' || allow_u || ')'; + + msg := '-2- ' || directed || ', no reverse_cost: from 1 to ' || i || ' to ' || j || msg_end; + RETURN query SELECT set_eq(withPoints_sql, dijkstraVia_sql, msg); + + k := k + 1; END LOOP; From 287b19cefb004eb4645e493524c4458185e87405 Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 10:44:08 -0600 Subject: [PATCH 09/14] [queries][trspVia_withPoints] adding documentation queries --- docqueries/trsp/CMakeLists.txt | 1 + docqueries/trsp/test.conf | 2 + docqueries/trsp/trspVia_withPoints.result | 333 ++++++++++++++++++++ docqueries/trsp/trspVia_withPoints.test.sql | 136 ++++++++ docqueries/trsp/trsp_withPoints.result | 157 +++++---- 5 files changed, 565 insertions(+), 64 deletions(-) create mode 100644 docqueries/trsp/trspVia_withPoints.result create mode 100644 docqueries/trsp/trspVia_withPoints.test.sql diff --git a/docqueries/trsp/CMakeLists.txt b/docqueries/trsp/CMakeLists.txt index 59be29c8111..e5f63b75888 100644 --- a/docqueries/trsp/CMakeLists.txt +++ b/docqueries/trsp/CMakeLists.txt @@ -4,6 +4,7 @@ SET(LOCAL_FILES trsp_notes trsp_withPoints trspVia + trspVia_withPoints ) foreach (f ${LOCAL_FILES}) diff --git a/docqueries/trsp/test.conf b/docqueries/trsp/test.conf index 583ce8b404f..a95a3b4f22a 100644 --- a/docqueries/trsp/test.conf +++ b/docqueries/trsp/test.conf @@ -9,6 +9,7 @@ doc-trsp trsp_withPoints trspVia + trspVia_withPoints trsp-any-02 trsp_vias-any-04 issue693 @@ -21,6 +22,7 @@ doc-trsp trsp_withPoints trspVia + trspVia_withPoints trsp_notes )] }, diff --git a/docqueries/trsp/trspVia_withPoints.result b/docqueries/trsp/trspVia_withPoints.result new file mode 100644 index 00000000000..82814d1e8ca --- /dev/null +++ b/docqueries/trsp/trspVia_withPoints.result @@ -0,0 +1,333 @@ +BEGIN; +BEGIN +SET client_min_messages TO NOTICE; +SET +SET extra_float_digits=-3; +SET +/* -- q0 */ +SELECT * FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[-6, 4, -5]); + seq | path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost | route_agg_cost +-----+---------+----------+-----------+---------+------+------+------+----------+---------------- + 1 | 1 | 1 | -6 | 4 | -6 | 4 | 0.3 | 0 | 0 + 2 | 1 | 2 | -6 | 4 | 5 | 10 | 1 | 0.3 | 0.3 + 3 | 1 | 3 | -6 | 4 | 10 | 12 | 1 | 1.3 | 1.3 + 4 | 1 | 4 | -6 | 4 | 11 | 13 | 1 | 2.3 | 2.3 + 5 | 1 | 5 | -6 | 4 | 12 | 15 | 1 | 3.3 | 3.3 + 6 | 1 | 6 | -6 | 4 | 9 | 16 | 1 | 4.3 | 4.3 + 7 | 1 | 7 | -6 | 4 | 4 | -1 | 0 | 5.3 | 5.3 + 8 | 2 | 1 | 4 | -5 | 4 | 3 | 1 | 0 | 5.3 + 9 | 2 | 2 | 4 | -5 | 3 | 5 | 0.8 | 1 | 6.3 + 10 | 2 | 3 | 4 | -5 | -5 | -2 | 0 | 1.8 | 7.1 +(10 rows) + +SELECT * FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + -6, 4); + seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost +-----+----------+-----------+---------+------+------+------+---------- + 1 | 1 | -6 | 4 | -6 | 4 | 0.3 | 0 + 2 | 2 | -6 | 4 | 5 | 10 | 1 | 0.3 + 3 | 3 | -6 | 4 | 10 | 12 | 1 | 1.3 + 4 | 4 | -6 | 4 | 11 | 13 | 1 | 2.3 + 5 | 5 | -6 | 4 | 12 | 15 | 1 | 3.3 + 6 | 6 | -6 | 4 | 9 | 16 | 1 | 4.3 + 7 | 7 | -6 | 4 | 4 | -1 | 0 | 5.3 +(7 rows) + +SELECT * FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + 2, 4); + seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost +-----+----------+-----------+---------+------+------+------+---------- + 1 | 1 | 2 | 4 | 2 | 4 | 1 | 0 + 2 | 2 | 2 | 4 | 5 | 10 | 1 | 1 + 3 | 3 | 2 | 4 | 10 | 12 | 1 | 2 + 4 | 4 | 2 | 4 | 11 | 13 | 1 | 3 + 5 | 5 | 2 | 4 | 12 | 15 | 1 | 4 + 6 | 6 | 2 | 4 | 9 | 16 | 1 | 5 + 7 | 7 | 2 | 4 | 4 | -1 | 0 | 6 +(7 rows) + +SELECT * FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + 5, 4); + seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost +-----+----------+-----------+---------+------+------+------+---------- + 1 | 1 | 5 | 4 | 5 | 10 | 1 | 0 + 2 | 2 | 5 | 4 | 10 | 12 | 1 | 1 + 3 | 3 | 5 | 4 | 11 | 13 | 1 | 2 + 4 | 4 | 5 | 4 | 12 | 15 | 1 | 3 + 5 | 5 | 5 | 4 | 9 | 16 | 1 | 4 + 6 | 6 | 5 | 4 | 4 | -1 | 0 | 5 +(6 rows) + +/* -- q1 */ +SELECT * FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[1, 5, 7, 10, 4] +); + seq | path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost | route_agg_cost +-----+---------+----------+-----------+---------+------+------+------+----------+---------------- + 1 | 1 | 1 | 1 | 5 | 1 | 1 | 1 | 0 | 0 + 2 | 1 | 2 | 1 | 5 | 2 | 4 | 1 | 1 | 1 + 3 | 1 | 3 | 1 | 5 | 5 | -1 | 0 | 2 | 2 + 4 | 2 | 1 | 5 | 7 | 5 | 7 | 1 | 0 | 2 + 5 | 2 | 2 | 5 | 7 | 8 | 6 | 1 | 1 | 3 + 6 | 2 | 3 | 5 | 7 | 7 | -1 | 0 | 2 | 4 + 7 | 3 | 1 | 7 | 10 | 7 | 6 | 1 | 0 | 4 + 8 | 3 | 2 | 7 | 10 | 8 | 7 | 1 | 1 | 5 + 9 | 3 | 3 | 7 | 10 | 5 | 4 | 0.6 | 2 | 6 + 10 | 3 | 4 | 7 | 10 | 5 | 10 | 1 | 2.6 | 6.6 + 11 | 3 | 5 | 7 | 10 | 10 | -1 | 0 | 3.6 | 7.6 + 12 | 4 | 1 | 10 | 4 | 10 | 12 | 1 | 0 | 7.6 + 13 | 4 | 2 | 10 | 4 | 11 | 13 | 1 | 1 | 8.6 + 14 | 4 | 3 | 10 | 4 | 12 | 15 | 1 | 2 | 9.6 + 15 | 4 | 4 | 10 | 4 | 9 | 16 | 1 | 3 | 10.6 + 16 | 4 | 5 | 10 | 4 | 4 | -2 | 0 | 4 | 11.6 +(16 rows) + +/* -- q2 */ +SELECT agg_cost FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[1, 5, 7, 10, 4] +) +WHERE path_id = 3 AND edge <0; + agg_cost +---------- + 3.6 +(1 row) + +/* -- q3 */ +SELECT route_agg_cost FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[1, 5, 7, 10, 4] +) +WHERE path_id = 3 AND edge < 0; + route_agg_cost +---------------- + 7.6 +(1 row) + +/* -- q4 */ +SELECT row_number() over () as node_seq, node +FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[1, 5, 7, 10, 4] +) +WHERE edge <> -1 ORDER BY seq; + node_seq | node +----------+------ + 1 | 1 + 2 | 2 + 3 | 5 + 4 | 8 + 5 | 7 + 6 | 8 + 7 | 5 + 8 | 5 + 9 | 10 + 10 | 11 + 11 | 12 + 12 | 9 + 13 | 4 +(13 rows) + +/* -- q5 */ +SELECT path_id, route_agg_cost FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[1, 5, 7, 10, 4] +) +WHERE edge < 0; + path_id | route_agg_cost +---------+---------------- + 1 | 2 + 2 | 4 + 3 | 7.6 + 4 | 11.6 +(4 rows) + +/* -- q6 */ +SELECT seq, route_agg_cost, node, agg_cost , +CASE WHEN edge = -1 THEN $$visits$$ +ELSE $$passes in front$$ +END as status +FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[1, 5, 7, 10, 4]) +WHERE agg_cost <> 0 or seq = 1; + seq | route_agg_cost | node | agg_cost | status +-----+----------------+------+----------+----------------- + 1 | 0 | 1 | 0 | passes in front + 2 | 1 | 2 | 1 | passes in front + 3 | 2 | 5 | 2 | visits + 5 | 3 | 8 | 1 | passes in front + 6 | 4 | 7 | 2 | visits + 8 | 5 | 8 | 1 | passes in front + 9 | 6 | 5 | 2 | passes in front + 10 | 6.6 | 5 | 2.6 | passes in front + 11 | 7.6 | 10 | 3.6 | visits + 13 | 8.6 | 11 | 1 | passes in front + 14 | 9.6 | 12 | 2 | passes in front + 15 | 10.6 | 9 | 3 | passes in front + 16 | 11.6 | 4 | 4 | passes in front +(13 rows) + +/* -- q7 */ +SELECT * FROM pgr_withPointsVia( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[-6, 4, -5]); + seq | path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost | route_agg_cost +-----+---------+----------+-----------+---------+------+------+------+----------+---------------- + 1 | 1 | 1 | -6 | 4 | -6 | 4 | 0.3 | 0 | 0 + 2 | 1 | 2 | -6 | 4 | 5 | 8 | 1 | 0.3 | 0.3 + 3 | 1 | 3 | -6 | 4 | 6 | 9 | 1 | 1.3 | 1.3 + 4 | 1 | 4 | -6 | 4 | 9 | 16 | 1 | 2.3 | 2.3 + 5 | 1 | 5 | -6 | 4 | 4 | -1 | 0 | 3.3 | 3.3 + 6 | 2 | 1 | 4 | -5 | 4 | 3 | 1 | 0 | 3.3 + 7 | 2 | 2 | 4 | -5 | 3 | 5 | 0.8 | 1 | 4.3 + 8 | 2 | 3 | 4 | -5 | -5 | -2 | 0 | 1.8 | 5.1 +(8 rows) + +/* -- q8 */ +SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost +FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + -6, 4); + path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost +---------+----------+-----------+---------+------+------+------+---------- + 1 | 1 | -6 | 4 | -6 | 4 | 0.3 | 0 + 1 | 2 | -6 | 4 | 5 | 10 | 1 | 0.3 + 1 | 3 | -6 | 4 | 10 | 12 | 1 | 1.3 + 1 | 4 | -6 | 4 | 11 | 13 | 1 | 2.3 + 1 | 5 | -6 | 4 | 12 | 15 | 1 | 3.3 + 1 | 6 | -6 | 4 | 9 | 16 | 1 | 4.3 + 1 | 7 | -6 | 4 | 4 | -1 | 0 | 5.3 +(7 rows) + +/* -- q9 */ +WITH +solutions AS ( + SELECT path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost + FROM pgr_withPointsVia( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[-6, 4, -5]) WHERE path_id != 1 + UNION + SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost + FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + -6, 4)), +with_seq AS ( + SELECT row_number() over(ORDER BY path_id, path_seq) AS seq, * + FROM solutions), +aggregation AS (SELECT seq, SUM(cost) OVER(ORDER BY seq) AS route_agg_cost FROM with_seq) +SELECT with_seq.*, COALESCE(route_agg_cost, 0) AS route_agg_cost +FROM with_seq LEFT JOIN aggregation ON (with_seq.seq = aggregation.seq + 1); + seq | path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost | route_agg_cost +-----+---------+----------+-----------+---------+------+------+------+----------+---------------- + 1 | 1 | 1 | -6 | 4 | -6 | 4 | 0.3 | 0 | 0 + 2 | 1 | 2 | -6 | 4 | 5 | 10 | 1 | 0.3 | 0.3 + 3 | 1 | 3 | -6 | 4 | 10 | 12 | 1 | 1.3 | 1.3 + 4 | 1 | 4 | -6 | 4 | 11 | 13 | 1 | 2.3 | 2.3 + 5 | 1 | 5 | -6 | 4 | 12 | 15 | 1 | 3.3 | 3.3 + 6 | 1 | 6 | -6 | 4 | 9 | 16 | 1 | 4.3 | 4.3 + 7 | 1 | 7 | -6 | 4 | 4 | -1 | 0 | 5.3 | 5.3 + 8 | 2 | 1 | 4 | -5 | 4 | 3 | 1 | 0 | 5.3 + 9 | 2 | 2 | 4 | -5 | 3 | 5 | 0.8 | 1 | 6.3 + 10 | 2 | 3 | 4 | -5 | -5 | -2 | 0 | 1.8 | 7.1 +(10 rows) + +/* -- q10 */ +SELECT * FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[-6, 4, -5]); + seq | path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost | route_agg_cost +-----+---------+----------+-----------+---------+------+------+------+----------+---------------- + 1 | 1 | 1 | -6 | 4 | -6 | 4 | 0.3 | 0 | 0 + 2 | 1 | 2 | -6 | 4 | 5 | 10 | 1 | 0.3 | 0.3 + 3 | 1 | 3 | -6 | 4 | 10 | 12 | 1 | 1.3 | 1.3 + 4 | 1 | 4 | -6 | 4 | 11 | 13 | 1 | 2.3 | 2.3 + 5 | 1 | 5 | -6 | 4 | 12 | 15 | 1 | 3.3 | 3.3 + 6 | 1 | 6 | -6 | 4 | 9 | 16 | 1 | 4.3 | 4.3 + 7 | 1 | 7 | -6 | 4 | 4 | -1 | 0 | 5.3 | 5.3 + 8 | 2 | 1 | 4 | -5 | 4 | 3 | 1 | 0 | 5.3 + 9 | 2 | 2 | 4 | -5 | 3 | 5 | 0.8 | 1 | 6.3 + 10 | 2 | 3 | 4 | -5 | -5 | -2 | 0 | 1.8 | 7.1 +(10 rows) + +/* -- q11 */ +SELECT * FROM pgr_withPointsVia( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[2, 5, 2], U_turn_on_edge => false); + seq | path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost | route_agg_cost +-----+---------+----------+-----------+---------+------+------+------+----------+---------------- + 1 | 1 | 1 | 2 | 5 | 2 | 4 | 1 | 0 | 0 + 2 | 1 | 2 | 2 | 5 | 5 | -1 | 0 | 1 | 1 + 3 | 2 | 1 | 5 | 2 | 5 | 8 | 1 | 0 | 1 + 4 | 2 | 2 | 5 | 2 | 6 | 9 | 1 | 1 | 2 + 5 | 2 | 3 | 5 | 2 | 9 | 16 | 1 | 2 | 3 + 6 | 2 | 4 | 5 | 2 | 4 | 3 | 1 | 3 | 4 + 7 | 2 | 5 | 5 | 2 | 3 | 2 | 1 | 4 | 5 + 8 | 2 | 6 | 5 | 2 | 2 | -2 | 0 | 5 | 6 +(8 rows) + +/* -- q12 */ +SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost +FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + 5, 2); + path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost +---------+----------+-----------+---------+------+------+------+---------- + 1 | 1 | 5 | 2 | 5 | 4 | 1 | 0 + 1 | 2 | 5 | 2 | 2 | -1 | 0 | 1 +(2 rows) + +/* -- q13 */ +SELECT * FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[2, 5, 2], U_turn_on_edge => false); + seq | path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost | route_agg_cost +-----+---------+----------+-----------+---------+------+------+------+----------+---------------- + 1 | 1 | 1 | 2 | 5 | 2 | 4 | 1 | 0 | 0 + 2 | 1 | 2 | 2 | 5 | 5 | -1 | 0 | 1 | 1 + 3 | 2 | 1 | 5 | 2 | 5 | 4 | 1 | 0 | 1 + 4 | 2 | 2 | 5 | 2 | 2 | -2 | 0 | 1 | 2 +(4 rows) + +/* -- q14 */ +ROLLBACK; +ROLLBACK diff --git a/docqueries/trsp/trspVia_withPoints.test.sql b/docqueries/trsp/trspVia_withPoints.test.sql new file mode 100644 index 00000000000..dc592f6209b --- /dev/null +++ b/docqueries/trsp/trspVia_withPoints.test.sql @@ -0,0 +1,136 @@ +SET extra_float_digits=-3; +-- documentation queries +/* -- q0 */ +SELECT * FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[-6, 4, -5]); +SELECT * FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + -6, 4); +SELECT * FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + 2, 4); +SELECT * FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + 5, 4); + +/* -- q1 */ +SELECT * FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[1, 5, 7, 10, 4] +); +/* -- q2 */ +SELECT agg_cost FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[1, 5, 7, 10, 4] +) +WHERE path_id = 3 AND edge <0; +/* -- q3 */ +SELECT route_agg_cost FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[1, 5, 7, 10, 4] +) +WHERE path_id = 3 AND edge < 0; +/* -- q4 */ +SELECT row_number() over () as node_seq, node +FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[1, 5, 7, 10, 4] +) +WHERE edge <> -1 ORDER BY seq; +/* -- q5 */ +SELECT path_id, route_agg_cost FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[1, 5, 7, 10, 4] +) +WHERE edge < 0; +/* -- q6 */ +SELECT seq, route_agg_cost, node, agg_cost , +CASE WHEN edge = -1 THEN $$visits$$ +ELSE $$passes in front$$ +END as status +FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[1, 5, 7, 10, 4]) +WHERE agg_cost <> 0 or seq = 1; +/* -- q7 */ +SELECT * FROM pgr_withPointsVia( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[-6, 4, -5]); +/* -- q8 */ +SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost +FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + -6, 4); +/* -- q9 */ +WITH +solutions AS ( + SELECT path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost + FROM pgr_withPointsVia( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[-6, 4, -5]) WHERE path_id != 1 + + UNION + + SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost + FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + -6, 4)), +with_seq AS ( + SELECT row_number() over(ORDER BY path_id, path_seq) AS seq, * + FROM solutions), +aggregation AS (SELECT seq, SUM(cost) OVER(ORDER BY seq) AS route_agg_cost FROM with_seq) + +SELECT with_seq.*, COALESCE(route_agg_cost, 0) AS route_agg_cost +FROM with_seq LEFT JOIN aggregation ON (with_seq.seq = aggregation.seq + 1); +/* -- q10 */ +SELECT * FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[-6, 4, -5]); +/* -- q11 */ +SELECT * FROM pgr_withPointsVia( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[2, 5, 2], U_turn_on_edge => false); +/* -- q12 */ +SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost +FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + 5, 2); +/* -- q13 */ +SELECT * FROM pgr_trspVia_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT path, cost FROM restrictions$$, + $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, + ARRAY[2, 5, 2], U_turn_on_edge => false); +/* -- q14 */ diff --git a/docqueries/trsp/trsp_withPoints.result b/docqueries/trsp/trsp_withPoints.result index 8e9926a4ef4..d32d0678c86 100644 --- a/docqueries/trsp/trsp_withPoints.result +++ b/docqueries/trsp/trsp_withPoints.result @@ -12,13 +12,12 @@ SELECT * FROM pgr_trsp_withPoints( -1, -3); seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost -----+----------+-----------+---------+------+------+------+---------- - 1 | 1 | -1 | -3 | -1 | 1 | 0.6 | 0 - 2 | 2 | -1 | -3 | 2 | 4 | 0.7 | 0.6 - 3 | 3 | -1 | -3 | -6 | 4 | 0.3 | 1.3 - 4 | 4 | -1 | -3 | 5 | 10 | 1 | 1.6 - 5 | 5 | -1 | -3 | 10 | 12 | 0.6 | 2.6 - 6 | 6 | -1 | -3 | -3 | -1 | 0 | 3.2 -(6 rows) + 1 | 1 | -1 | -3 | -1 | 1 | 1.4 | 0 + 2 | 2 | -1 | -3 | 2 | 4 | 1 | 1.4 + 3 | 3 | -1 | -3 | 5 | 10 | 1 | 2.4 + 4 | 4 | -1 | -3 | 10 | 12 | 0.6 | 3.4 + 5 | 5 | -1 | -3 | -3 | -1 | 0 | 4 +(5 rows) /* --e2 */ SELECT * FROM pgr_trsp_withPoints( @@ -29,7 +28,19 @@ SELECT * FROM pgr_trsp_withPoints( details := true); seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost -----+----------+-----------+---------+------+------+------+---------- -(0 rows) + 1 | 1 | -1 | 3 | -1 | 1 | 0.4 | 0 + 2 | 2 | -1 | 3 | 1 | 1 | 1 | 0.4 + 3 | 3 | -1 | 3 | 2 | 4 | 0.7 | 1.4 + 4 | 4 | -1 | 3 | -6 | 4 | 0.3 | 2.1 + 5 | 5 | -1 | 3 | 5 | 10 | 1 | 2.4 + 6 | 6 | -1 | 3 | 10 | 12 | 0.6 | 3.4 + 7 | 7 | -1 | 3 | -3 | 12 | 0.4 | 4 + 8 | 8 | -1 | 3 | 11 | 13 | 1 | 4.4 + 9 | 9 | -1 | 3 | 12 | 15 | 1 | 5.4 + 10 | 10 | -1 | 3 | 9 | 16 | 1 | 6.4 + 11 | 11 | -1 | 3 | 4 | 3 | 1 | 7.4 + 12 | 12 | -1 | 3 | 3 | -1 | 0 | 8.4 +(12 rows) /* --e3 */ SELECT * FROM pgr_trsp_withPoints( @@ -39,17 +50,15 @@ SELECT * FROM pgr_trsp_withPoints( -1, ARRAY[-3,5]); seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost -----+----------+-----------+---------+------+------+------+---------- - 1 | 1 | -1 | -3 | -1 | 1 | 0.6 | 0 - 2 | 2 | -1 | -3 | 2 | 4 | 0.7 | 0.6 - 3 | 3 | -1 | -3 | -6 | 4 | 0.3 | 1.3 - 4 | 4 | -1 | -3 | 5 | 10 | 1 | 1.6 - 5 | 5 | -1 | -3 | 10 | 12 | 0.6 | 2.6 - 6 | 6 | -1 | -3 | -3 | -1 | 0 | 3.2 - 7 | 1 | -1 | 5 | -1 | 1 | 0.6 | 0 - 8 | 2 | -1 | 5 | 2 | 4 | 0.7 | 0.6 - 9 | 3 | -1 | 5 | -6 | 4 | 0.3 | 1.3 - 10 | 4 | -1 | 5 | 5 | -1 | 0 | 1.6 -(10 rows) + 1 | 1 | -1 | -3 | -1 | 1 | 1.4 | 0 + 2 | 2 | -1 | -3 | 2 | 4 | 1 | 1.4 + 3 | 3 | -1 | -3 | 5 | 10 | 1 | 2.4 + 4 | 4 | -1 | -3 | 10 | 12 | 0.6 | 3.4 + 5 | 5 | -1 | -3 | -3 | -1 | 0 | 4 + 6 | 1 | -1 | 5 | -1 | 1 | 1.4 | 0 + 7 | 2 | -1 | 5 | 2 | 4 | 1 | 1.4 + 8 | 3 | -1 | 5 | 5 | -1 | 0 | 2.4 +(8 rows) /* --e4 */ SELECT * FROM pgr_trsp_withPoints( @@ -59,18 +68,16 @@ SELECT * FROM pgr_trsp_withPoints( ARRAY[-1,2], -3); seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost -----+----------+-----------+---------+------+------+------+---------- - 1 | 1 | -1 | -3 | -1 | 1 | 0.6 | 0 - 2 | 2 | -1 | -3 | 2 | 4 | 0.7 | 0.6 - 3 | 3 | -1 | -3 | -6 | 4 | 0.3 | 1.3 - 4 | 4 | -1 | -3 | 5 | 10 | 1 | 1.6 - 5 | 5 | -1 | -3 | 10 | 12 | 0.6 | 2.6 - 6 | 6 | -1 | -3 | -3 | -1 | 0 | 3.2 - 7 | 1 | 2 | -3 | 2 | 4 | 0.7 | 0 - 8 | 2 | 2 | -3 | -6 | 4 | 0.3 | 0.7 - 9 | 3 | 2 | -3 | 5 | 10 | 1 | 1 - 10 | 4 | 2 | -3 | 10 | 12 | 0.6 | 2 - 11 | 5 | 2 | -3 | -3 | -1 | 0 | 2.6 -(11 rows) + 1 | 1 | -1 | -3 | -1 | 1 | 1.4 | 0 + 2 | 2 | -1 | -3 | 2 | 4 | 1 | 1.4 + 3 | 3 | -1 | -3 | 5 | 10 | 1 | 2.4 + 4 | 4 | -1 | -3 | 10 | 12 | 0.6 | 3.4 + 5 | 5 | -1 | -3 | -3 | -1 | 0 | 4 + 6 | 1 | 2 | -3 | 2 | 4 | 1 | 0 + 7 | 2 | 2 | -3 | 5 | 10 | 1 | 1 + 8 | 3 | 2 | -3 | 10 | 12 | 0.6 | 2 + 9 | 4 | 2 | -3 | -3 | -1 | 0 | 2.6 +(9 rows) /* --e5 */ SELECT * FROM pgr_trsp_withPoints( @@ -80,31 +87,37 @@ SELECT * FROM pgr_trsp_withPoints( ARRAY[-1,2], ARRAY[-3,7]); seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost -----+----------+-----------+---------+------+------+------+---------- - 1 | 1 | -1 | -3 | -1 | 1 | 0.6 | 0 - 2 | 2 | -1 | -3 | 2 | 4 | 0.7 | 0.6 - 3 | 3 | -1 | -3 | -6 | 4 | 0.3 | 1.3 - 4 | 4 | -1 | -3 | 5 | 10 | 1 | 1.6 - 5 | 5 | -1 | -3 | 10 | 12 | 0.6 | 2.6 - 6 | 6 | -1 | -3 | -3 | -1 | 0 | 3.2 - 7 | 1 | -1 | 7 | -1 | 1 | 0.6 | 0 - 8 | 2 | -1 | 7 | 2 | 4 | 0.7 | 0.6 - 9 | 3 | -1 | 7 | -6 | 4 | 0.3 | 1.3 - 10 | 4 | -1 | 7 | 5 | 7 | 1 | 1.6 - 11 | 5 | -1 | 7 | 8 | 6 | 0.7 | 2.6 - 12 | 6 | -1 | 7 | -4 | 6 | 0.3 | 3.3 - 13 | 7 | -1 | 7 | 7 | -1 | 0 | 3.6 - 14 | 1 | 2 | -3 | 2 | 4 | 0.7 | 0 - 15 | 2 | 2 | -3 | -6 | 4 | 0.3 | 0.7 - 16 | 3 | 2 | -3 | 5 | 10 | 1 | 1 - 17 | 4 | 2 | -3 | 10 | 12 | 0.6 | 2 - 18 | 5 | 2 | -3 | -3 | -1 | 0 | 2.6 - 19 | 1 | 2 | 7 | 2 | 4 | 0.7 | 0 - 20 | 2 | 2 | 7 | -6 | 4 | 0.3 | 0.7 - 21 | 3 | 2 | 7 | 5 | 7 | 1 | 1 - 22 | 4 | 2 | 7 | 8 | 6 | 0.7 | 2 - 23 | 5 | 2 | 7 | -4 | 6 | 0.3 | 2.7 - 24 | 6 | 2 | 7 | 7 | -1 | 0 | 3 -(24 rows) + 1 | 1 | -1 | -3 | -1 | 1 | 1.4 | 0 + 2 | 2 | -1 | -3 | 2 | 4 | 1 | 1.4 + 3 | 3 | -1 | -3 | 5 | 10 | 1 | 2.4 + 4 | 4 | -1 | -3 | 10 | 12 | 0.6 | 3.4 + 5 | 5 | -1 | -3 | -3 | -1 | 0 | 4 + 6 | 1 | -1 | 7 | -1 | 1 | 1.4 | 0 + 7 | 2 | -1 | 7 | 2 | 4 | 1 | 1.4 + 8 | 3 | -1 | 7 | 5 | 10 | 1 | 2.4 + 9 | 4 | -1 | 7 | 10 | 12 | 1 | 3.4 + 10 | 5 | -1 | 7 | 11 | 13 | 1 | 4.4 + 11 | 6 | -1 | 7 | 12 | 15 | 1 | 5.4 + 12 | 7 | -1 | 7 | 9 | 9 | 1 | 6.4 + 13 | 8 | -1 | 7 | 6 | 8 | 1 | 7.4 + 14 | 9 | -1 | 7 | 5 | 7 | 1 | 8.4 + 15 | 10 | -1 | 7 | 8 | 6 | 1 | 9.4 + 16 | 11 | -1 | 7 | 7 | -1 | 0 | 10.4 + 17 | 1 | 2 | -3 | 2 | 4 | 1 | 0 + 18 | 2 | 2 | -3 | 5 | 10 | 1 | 1 + 19 | 3 | 2 | -3 | 10 | 12 | 0.6 | 2 + 20 | 4 | 2 | -3 | -3 | -1 | 0 | 2.6 + 21 | 1 | 2 | 7 | 2 | 4 | 1 | 0 + 22 | 2 | 2 | 7 | 5 | 10 | 1 | 1 + 23 | 3 | 2 | 7 | 10 | 12 | 1 | 2 + 24 | 4 | 2 | 7 | 11 | 13 | 1 | 3 + 25 | 5 | 2 | 7 | 12 | 15 | 1 | 4 + 26 | 6 | 2 | 7 | 9 | 9 | 1 | 5 + 27 | 7 | 2 | 7 | 6 | 8 | 1 | 6 + 28 | 8 | 2 | 7 | 5 | 7 | 1 | 7 + 29 | 9 | 2 | 7 | 8 | 6 | 1 | 8 + 30 | 10 | 2 | 7 | 7 | -1 | 0 | 9 +(30 rows) /* --q2 */ SELECT ($$($$ || start_vid || $$ => $$ || end_vid ||$$) at $$ || path_seq || $$th step:$$)::TEXT AS path_at, @@ -129,15 +142,17 @@ ELSE $$ passes in front of$$ (-1 => -3) at 4th step: | passes in front of | Point | 6 (-1 => -2) at 4th step: | passes in front of | Point | 6 (-1 => -2) at 6th step: | passes in front of | Vertex | 6 + (-1 => 3) at 4th step: | passes in front of | Point | 6 (-1 => 6) at 4th step: | passes in front of | Point | 6 (-1 => 6) at 6th step: | visits | Vertex | 6 (1 => -6) at 3th step: | visits | Point | 6 (1 => -3) at 3th step: | passes in front of | Point | 6 (1 => -2) at 3th step: | passes in front of | Point | 6 (1 => -2) at 5th step: | passes in front of | Vertex | 6 + (1 => 3) at 3th step: | passes in front of | Point | 6 (1 => 6) at 3th step: | passes in front of | Point | 6 (1 => 6) at 5th step: | visits | Vertex | 6 -(12 rows) +(14 rows) /* --q3 */ SELECT ($$($$ || start_vid || $$ => $$ || end_vid ||$$) at $$ || path_seq || $$th step:$$)::TEXT AS path_at, @@ -162,15 +177,17 @@ ELSE $$ passes in front of$$ (-1 => -3) at 3th step: | passes in front of | Point | 6 (-1 => -2) at 3th step: | passes in front of | Point | 6 (-1 => -2) at 5th step: | passes in front of | Vertex | 6 + (-1 => 3) at 3th step: | passes in front of | Point | 6 (-1 => 6) at 3th step: | passes in front of | Point | 6 (-1 => 6) at 5th step: | visits | Vertex | 6 (1 => -6) at 4th step: | visits | Point | 6 (1 => -3) at 4th step: | passes in front of | Point | 6 (1 => -2) at 4th step: | passes in front of | Point | 6 (1 => -2) at 6th step: | passes in front of | Vertex | 6 + (1 => 3) at 4th step: | passes in front of | Point | 6 (1 => 6) at 4th step: | passes in front of | Point | 6 (1 => 6) at 6th step: | visits | Vertex | 6 -(12 rows) +(14 rows) /* --q4 */ SELECT * FROM pgr_trsp_withPoints( @@ -218,12 +235,24 @@ SELECT * FROM pgr_trsp_withPoints( details => true); seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost -----+----------+-----------+---------+------+------+------+---------- - 1 | 1 | 2 | -3 | 2 | 4 | 0.7 | 0 - 2 | 2 | 2 | -3 | -6 | 4 | 0.3 | 0.7 - 3 | 3 | 2 | -3 | 5 | 10 | 1 | 1 - 4 | 4 | 2 | -3 | 10 | 12 | 0.6 | 2 - 5 | 5 | 2 | -3 | -3 | -1 | 0 | 2.6 -(5 rows) + 1 | 1 | -1 | 3 | -1 | 1 | 0.4 | 0 + 2 | 2 | -1 | 3 | 1 | 1 | 1 | 0.4 + 3 | 3 | -1 | 3 | 2 | 4 | 0.7 | 1.4 + 4 | 4 | -1 | 3 | -6 | 4 | 0.3 | 2.1 + 5 | 5 | -1 | 3 | 5 | 10 | 1 | 2.4 + 6 | 6 | -1 | 3 | 10 | 12 | 0.6 | 3.4 + 7 | 7 | -1 | 3 | -3 | 12 | 0.4 | 4 + 8 | 8 | -1 | 3 | 11 | 13 | 1 | 4.4 + 9 | 9 | -1 | 3 | 12 | 15 | 1 | 5.4 + 10 | 10 | -1 | 3 | 9 | 16 | 1 | 6.4 + 11 | 11 | -1 | 3 | 4 | 3 | 1 | 7.4 + 12 | 12 | -1 | 3 | 3 | -1 | 0 | 8.4 + 13 | 1 | 2 | -3 | 2 | 4 | 0.7 | 0 + 14 | 2 | 2 | -3 | -6 | 4 | 0.3 | 0.7 + 15 | 3 | 2 | -3 | 5 | 10 | 1 | 1 + 16 | 4 | 2 | -3 | 10 | 12 | 0.6 | 2 + 17 | 5 | 2 | -3 | -3 | -1 | 0 | 2.6 +(17 rows) /* --q6 */ ROLLBACK; From cc488718c359d7b7a2d18fbeca88127a52a38139 Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 10:47:46 -0600 Subject: [PATCH 10/14] [queries][trsp] adjusting results because of bug fix --- docqueries/trsp/doc-trsp.result | 1 + docqueries/trsp/issue717.result | 12 ++++++++++++ docqueries/trsp/trsp_notes.result | 15 ++++++++++----- docqueries/trsp/trsp_vias-any-04.result | 3 +++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/docqueries/trsp/doc-trsp.result b/docqueries/trsp/doc-trsp.result index 71ca9aed349..1b0aee03e73 100644 --- a/docqueries/trsp/doc-trsp.result +++ b/docqueries/trsp/doc-trsp.result @@ -213,6 +213,7 @@ WARNING: pgr_trspViaVertices(text,anyarray,boolean,boolean,text) is been deprec true, 'SELECT to_cost, target_id::int4, FROM_edge || coalesce('',''||via_path,'''') AS via_path FROM old_restrictions'); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated seq | id1 | id2 | id3 | cost diff --git a/docqueries/trsp/issue717.result b/docqueries/trsp/issue717.result index 9f2168fe9da..ee504d6824b 100644 --- a/docqueries/trsp/issue717.result +++ b/docqueries/trsp/issue717.result @@ -58,6 +58,7 @@ SELECT * FROM pgr_trspViaEdges( true, true ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 1 | -1 | 4 | 0.5 @@ -78,6 +79,7 @@ SELECT * FROM pgr_trspViaEdges( true, true ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 1 | -1 | 1 | 0.5 @@ -102,6 +104,7 @@ SELECT * FROM pgr_trspViaEdges( true, true ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 1 | -1 | 1 | 0.5 @@ -137,6 +140,7 @@ SELECT * FROM pgr_trspViaEdges( true, false ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 2 | -2 | 6 | 0.5 @@ -153,6 +157,7 @@ SELECT * FROM pgr_trspViaEdges( true, false ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 1 | -1 | 1 | 0.5 @@ -169,6 +174,7 @@ SELECT * FROM pgr_trspViaEdges( true, false ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 1 | -1 | 1 | 0.5 @@ -204,6 +210,7 @@ SELECT * FROM pgr_trspViaEdges( false, true ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 1 | -1 | 4 | 0.5 @@ -224,6 +231,7 @@ SELECT * FROM pgr_trspViaEdges( false, true ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 1 | -1 | 1 | 0.5 @@ -245,6 +253,7 @@ SELECT * FROM pgr_trspViaEdges( false, true ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 1 | -1 | 1 | 0.5 @@ -285,6 +294,7 @@ SELECT * FROM pgr_trspViaEdges( false, false ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 1 | -1 | 4 | 0.5 @@ -305,6 +315,7 @@ SELECT * FROM pgr_trspViaEdges( false, false ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 1 | -1 | 1 | 0.5 @@ -326,6 +337,7 @@ SELECT * FROM pgr_trspViaEdges( false, false ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 1 | -1 | 1 | 0.5 diff --git a/docqueries/trsp/trsp_notes.result b/docqueries/trsp/trsp_notes.result index 5935aecaac1..077f732c16f 100644 --- a/docqueries/trsp/trsp_notes.result +++ b/docqueries/trsp/trsp_notes.result @@ -404,11 +404,10 @@ SELECT * FROM pgr_trsp_withPoints( seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost -----+----------+-----------+---------+------+------+------+---------- 1 | 1 | 6 | -1 | 6 | 8 | 1 | 0 - 2 | 2 | 6 | -1 | 5 | 4 | 0.3 | 1 - 3 | 3 | 6 | -1 | -6 | 4 | 0.7 | 1.3 - 4 | 4 | 6 | -1 | 2 | 1 | 0.6 | 2 - 5 | 5 | 6 | -1 | -1 | -1 | 0 | 2.6 -(5 rows) + 2 | 2 | 6 | -1 | 5 | 4 | 1 | 1 + 3 | 3 | 6 | -1 | 2 | 1 | 0.6 | 2 + 4 | 4 | 6 | -1 | -1 | -1 | 0 | 2.6 +(4 rows) /* --place26 */ SELECT * FROM pgr_trspViaVertices( @@ -545,6 +544,7 @@ SELECT * FROM pgr_trspViaEdges( false, true, $$SELECT 100::float AS to_cost, 25::INTEGER AS target_id, '32, 33'::TEXT AS via_path$$ ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated seq | id1 | id2 | id3 | cost @@ -579,6 +579,7 @@ SELECT * FROM pgr_trspViaEdges( ARRAY[1, 17, 1], ARRAY[0,0.5,0.5], false, true ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated seq | id1 | id2 | id3 | cost @@ -592,6 +593,7 @@ SELECT * FROM pgr_trspViaEdges( false, true ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ (0 rows) @@ -609,6 +611,7 @@ SELECT * FROM pgr_trspViaEdges( false, true ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated seq | id1 | id2 | id3 | cost -----+-----+-----+-----+------ 1 | 1 | -1 | 1 | 0.4 @@ -636,6 +639,7 @@ SELECT * FROM pgr_trspViaEdges( false, true, $$SELECT 100::float AS to_cost, 25::INTEGER AS target_id, '32, 33'::TEXT AS via_path$$ ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated seq | id1 | id2 | id3 | cost @@ -664,6 +668,7 @@ SELECT * FROM pgr_trspViaEdges( false, true ); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated seq | id1 | id2 | id3 | cost diff --git a/docqueries/trsp/trsp_vias-any-04.result b/docqueries/trsp/trsp_vias-any-04.result index 1e8e9941cab..8a709964258 100644 --- a/docqueries/trsp/trsp_vias-any-04.result +++ b/docqueries/trsp/trsp_vias-any-04.result @@ -62,6 +62,7 @@ SELECT * FROM pgr_trspViaEdges( true, 'SELECT to_cost, target_id::INTEGER, from_edge||coalesce('',''||via_path,'''') AS via_path FROM old_restrictions'); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated seq | id1 | id2 | id3 | cost @@ -92,6 +93,7 @@ SELECT * FROM pgr_trspViaEdges( true, 'SELECT to_cost, target_id::INTEGER, from_edge||coalesce('',''||via_path,'''') AS via_path FROM old_restrictions'); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated seq | id1 | id2 | id3 | cost @@ -120,6 +122,7 @@ SELECT * FROM pgr_trspViaEdges( true, 'SELECT to_cost, target_id::INTEGER, from_edge||coalesce('',''||via_path,'''') AS via_path FROM old_restrictions'); +WARNING: pgr_trspViaEdges(text, integer[], float[], boolean, boolean, text) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated WARNING: pgr_trsp(text,integer,float,integer,float,boolean,boolean) is been deprecated seq | id1 | id2 | id3 | cost From 7106e700a69444a57ea1b8ed7316b12b7de8a56e Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 10:49:05 -0600 Subject: [PATCH 11/14] [queries][trsp] adjusting queries within functions --- docqueries/trsp/trspVia.result | 12 +- docqueries/trsp/trspVia.test.sql | 12 +- docqueries/trsp/trsp_withPoints.result | 152 +++++++------------ docqueries/trsp/trsp_withPoints.test.sql | 70 +++------ docqueries/withPoints/withPointsVia.result | 21 ++- docqueries/withPoints/withPointsVia.test.sql | 2 +- 6 files changed, 97 insertions(+), 172 deletions(-) diff --git a/docqueries/trsp/trspVia.result b/docqueries/trsp/trspVia.result index 2722209ade9..62458972ca4 100644 --- a/docqueries/trsp/trspVia.result +++ b/docqueries/trsp/trspVia.result @@ -159,7 +159,7 @@ SELECT * FROM pgr_dijkstraVia( SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost FROM pgr_trsp( $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, $$SELECT path, cost FROM restrictions$$, - 2,8); + 2, 8); path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost ---------+----------+-----------+---------+------+------+------+---------- 1 | 1 | 2 | 8 | 2 | 4 | 1 | 0 @@ -183,7 +183,7 @@ solutions AS ( SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost FROM pgr_trsp( $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, $$SELECT path, cost FROM restrictions$$, - 2,8)), + 2, 8)), with_seq AS ( SELECT row_number() over(ORDER BY path_id, path_seq) AS seq, * FROM solutions), @@ -210,7 +210,7 @@ FROM with_seq LEFT JOIN aggregation ON (with_seq.seq = aggregation.seq + 1); SELECT * FROM pgr_trspVia( $$SELECT id, source, target, cost, reverse_cost FROM edge_table order by id$$, $$SELECT path, cost FROM restrictions$$, - ARRAY[2,8,2]); + ARRAY[2, 8, 2]); seq | path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost | route_agg_cost -----+---------+----------+-----------+---------+------+------+------+----------+---------------- 1 | 1 | 1 | 2 | 8 | 2 | 4 | 1 | 0 | 0 @@ -230,7 +230,7 @@ SELECT * FROM pgr_trspVia( /* -- q11 */ SELECT * FROM pgr_dijkstraVia( $$SELECT id, source, target, cost, reverse_cost FROM edge_table order by id$$, - ARRAY[2,5,2], U_turn_on_edge := false); + ARRAY[2, 5, 2], U_turn_on_edge => false); seq | path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost | route_agg_cost -----+---------+----------+-----------+---------+------+------+------+----------+---------------- 1 | 1 | 1 | 2 | 5 | 2 | 4 | 1 | 0 | 0 @@ -247,7 +247,7 @@ SELECT * FROM pgr_dijkstraVia( SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost FROM pgr_trsp( $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, $$SELECT path, cost FROM restrictions$$, - 5,2); + 5, 2); path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost ---------+----------+-----------+---------+------+------+------+---------- 1 | 1 | 5 | 2 | 5 | 4 | 1 | 0 @@ -258,7 +258,7 @@ SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost FR SELECT * FROM pgr_trspVia( $$SELECT id, source, target, cost, reverse_cost FROM edge_table order by id$$, $$SELECT path, cost FROM restrictions$$, - ARRAY[2,5,2], U_turn_on_edge := false); + ARRAY[2, 5, 2], U_turn_on_edge => false); seq | path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost | route_agg_cost -----+---------+----------+-----------+---------+------+------+------+----------+---------------- 1 | 1 | 1 | 2 | 5 | 2 | 4 | 1 | 0 | 0 diff --git a/docqueries/trsp/trspVia.test.sql b/docqueries/trsp/trspVia.test.sql index 7a940cb2c80..4f42145f2c2 100644 --- a/docqueries/trsp/trspVia.test.sql +++ b/docqueries/trsp/trspVia.test.sql @@ -58,7 +58,7 @@ SELECT * FROM pgr_dijkstraVia( SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost FROM pgr_trsp( $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, $$SELECT path, cost FROM restrictions$$, - 2,8); + 2, 8); /* -- q9 */ WITH solutions AS ( @@ -71,7 +71,7 @@ solutions AS ( SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost FROM pgr_trsp( $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, $$SELECT path, cost FROM restrictions$$, - 2,8)), + 2, 8)), with_seq AS ( SELECT row_number() over(ORDER BY path_id, path_seq) AS seq, * FROM solutions), @@ -83,19 +83,19 @@ FROM with_seq LEFT JOIN aggregation ON (with_seq.seq = aggregation.seq + 1); SELECT * FROM pgr_trspVia( $$SELECT id, source, target, cost, reverse_cost FROM edge_table order by id$$, $$SELECT path, cost FROM restrictions$$, - ARRAY[2,8,2]); + ARRAY[2, 8, 2]); /* -- q11 */ SELECT * FROM pgr_dijkstraVia( $$SELECT id, source, target, cost, reverse_cost FROM edge_table order by id$$, - ARRAY[2,5,2], U_turn_on_edge := false); + ARRAY[2, 5, 2], U_turn_on_edge => false); /* -- q12 */ SELECT 1 AS path_id, path_seq, start_vid, end_vid, node, edge, cost, agg_cost FROM pgr_trsp( $$SELECT id, source, target, cost, reverse_cost FROM edge_table$$, $$SELECT path, cost FROM restrictions$$, - 5,2); + 5, 2); /* -- q13 */ SELECT * FROM pgr_trspVia( $$SELECT id, source, target, cost, reverse_cost FROM edge_table order by id$$, $$SELECT path, cost FROM restrictions$$, - ARRAY[2,5,2], U_turn_on_edge := false); + ARRAY[2, 5, 2], U_turn_on_edge => false); /* -- q14 */ diff --git a/docqueries/trsp/trsp_withPoints.result b/docqueries/trsp/trsp_withPoints.result index d32d0678c86..ef68c4e767c 100644 --- a/docqueries/trsp/trsp_withPoints.result +++ b/docqueries/trsp/trsp_withPoints.result @@ -5,27 +5,12 @@ SET SET extra_float_digits=-3; SET /* --e1 */ -SELECT * FROM pgr_trsp_withPoints( - $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, - $$SELECT id, path, cost FROM restrictions$$, - $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - -1, -3); - seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost ------+----------+-----------+---------+------+------+------+---------- - 1 | 1 | -1 | -3 | -1 | 1 | 1.4 | 0 - 2 | 2 | -1 | -3 | 2 | 4 | 1 | 1.4 - 3 | 3 | -1 | -3 | 5 | 10 | 1 | 2.4 - 4 | 4 | -1 | -3 | 10 | 12 | 0.6 | 3.4 - 5 | 5 | -1 | -3 | -3 | -1 | 0 | 4 -(5 rows) - -/* --e2 */ SELECT * FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, -1, 3, - details := true); + details => true); seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost -----+----------+-----------+---------+------+------+------+---------- 1 | 1 | -1 | 3 | -1 | 1 | 0.4 | 0 @@ -42,12 +27,12 @@ SELECT * FROM pgr_trsp_withPoints( 12 | 12 | -1 | 3 | 3 | -1 | 0 | 8.4 (12 rows) -/* --e3 */ +/* --e2 */ SELECT * FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - -1, ARRAY[-3,5]); + -1, ARRAY[-3, 5]); seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost -----+----------+-----------+---------+------+------+------+---------- 1 | 1 | -1 | -3 | -1 | 1 | 1.4 | 0 @@ -60,12 +45,12 @@ SELECT * FROM pgr_trsp_withPoints( 8 | 3 | -1 | 5 | 5 | -1 | 0 | 2.4 (8 rows) -/* --e4 */ +/* --e3 */ SELECT * FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - ARRAY[-1,2], -3); + ARRAY[-1, 2], -3); seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost -----+----------+-----------+---------+------+------+------+---------- 1 | 1 | -1 | -3 | -1 | 1 | 1.4 | 0 @@ -79,12 +64,12 @@ SELECT * FROM pgr_trsp_withPoints( 9 | 4 | 2 | -3 | -3 | -1 | 0 | 2.6 (9 rows) -/* --e5 */ +/* --e4 */ SELECT * FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - ARRAY[-1,2], ARRAY[-3,7]); + ARRAY[-1, 2], ARRAY[-3, 7]); seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost -----+----------+-----------+---------+------+------+------+---------- 1 | 1 | -1 | -3 | -1 | 1 | 1.4 | 0 @@ -119,23 +104,52 @@ SELECT * FROM pgr_trsp_withPoints( 30 | 10 | 2 | 7 | 7 | -1 | 0 | 9 (30 rows) -/* --q2 */ -SELECT ($$($$ || start_vid || $$ => $$ || end_vid ||$$) at $$ || path_seq || $$th step:$$)::TEXT AS path_at, -CASE WHEN edge = -1 THEN $$ visits$$ -ELSE $$ passes in front of$$ +/* --e5 */ +SELECT * FROM pgr_trsp_withPoints( + $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, + $$SELECT id, path, cost FROM restrictions$$, + $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, + $$SELECT * FROM (VALUES (-1, 3), (2, -3)) AS t(source, target)$$, + driving_side => 'r', + details => true); + seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost +-----+----------+-----------+---------+------+------+------+---------- + 1 | 1 | -1 | 3 | -1 | 1 | 0.4 | 0 + 2 | 2 | -1 | 3 | 1 | 1 | 1 | 0.4 + 3 | 3 | -1 | 3 | 2 | 4 | 0.7 | 1.4 + 4 | 4 | -1 | 3 | -6 | 4 | 0.3 | 2.1 + 5 | 5 | -1 | 3 | 5 | 10 | 1 | 2.4 + 6 | 6 | -1 | 3 | 10 | 12 | 0.6 | 3.4 + 7 | 7 | -1 | 3 | -3 | 12 | 0.4 | 4 + 8 | 8 | -1 | 3 | 11 | 13 | 1 | 4.4 + 9 | 9 | -1 | 3 | 12 | 15 | 1 | 5.4 + 10 | 10 | -1 | 3 | 9 | 16 | 1 | 6.4 + 11 | 11 | -1 | 3 | 4 | 3 | 1 | 7.4 + 12 | 12 | -1 | 3 | 3 | -1 | 0 | 8.4 + 13 | 1 | 2 | -3 | 2 | 4 | 0.7 | 0 + 14 | 2 | 2 | -3 | -6 | 4 | 0.3 | 0.7 + 15 | 3 | 2 | -3 | 5 | 10 | 1 | 1 + 16 | 4 | 2 | -3 | 10 | 12 | 0.6 | 2 + 17 | 5 | 2 | -3 | -3 | -1 | 0 | 2.6 +(17 rows) + +/* --q1 */ +SELECT ('(' || start_vid || ' => ' || end_vid ||') at ' || path_seq || 'th step:')::TEXT AS path_at, +CASE WHEN edge = -1 THEN ' visits' +ELSE ' passes in front of' END as status, - CASE WHEN node < 0 THEN $$Point$$ - ELSE $$Vertex$$ + CASE WHEN node < 0 THEN 'Point' + ELSE 'Vertex' END as is_a, abs(node) as id FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - ARRAY[1,-1], ARRAY[-2,-3,-6,3,6], - driving_side := $$r$$, - details := true) - WHERE node IN (-6,6); + ARRAY[1,-1], ARRAY[-2, -3, -6, 3, 6], + driving_side => 'r', + details => true) + WHERE node IN (-6, 6); path_at | status | is_a | id -------------------------+---------------------+--------+---- (-1 => -6) at 4th step: | visits | Point | 6 @@ -154,49 +168,14 @@ ELSE $$ passes in front of$$ (1 => 6) at 5th step: | visits | Vertex | 6 (14 rows) -/* --q3 */ -SELECT ($$($$ || start_vid || $$ => $$ || end_vid ||$$) at $$ || path_seq || $$th step:$$)::TEXT AS path_at, -CASE WHEN edge = -1 THEN $$ visits$$ -ELSE $$ passes in front of$$ - END as status, - CASE WHEN node < 0 THEN $$Point$$ - ELSE $$Vertex$$ - END as is_a, - abs(node) as id - FROM pgr_trsp_withPoints( - $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, - $$SELECT id, path, cost FROM restrictions$$, - $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - ARRAY[1,-1], ARRAY[-2,-3,-6,3,6], - driving_side := $$l$$, - details := true) - WHERE node IN (-6,6); - path_at | status | is_a | id --------------------------+---------------------+--------+---- - (-1 => -6) at 3th step: | visits | Point | 6 - (-1 => -3) at 3th step: | passes in front of | Point | 6 - (-1 => -2) at 3th step: | passes in front of | Point | 6 - (-1 => -2) at 5th step: | passes in front of | Vertex | 6 - (-1 => 3) at 3th step: | passes in front of | Point | 6 - (-1 => 6) at 3th step: | passes in front of | Point | 6 - (-1 => 6) at 5th step: | visits | Vertex | 6 - (1 => -6) at 4th step: | visits | Point | 6 - (1 => -3) at 4th step: | passes in front of | Point | 6 - (1 => -2) at 4th step: | passes in front of | Point | 6 - (1 => -2) at 6th step: | passes in front of | Vertex | 6 - (1 => 3) at 4th step: | passes in front of | Point | 6 - (1 => 6) at 4th step: | passes in front of | Point | 6 - (1 => 6) at 6th step: | visits | Vertex | 6 -(14 rows) - -/* --q4 */ +/* --q2 */ SELECT * FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - ARRAY[-1,2], ARRAY[-3,7], - directed := false, - details := true); + ARRAY[-1, 2], ARRAY[-3, 7], + directed => false, + details => true); seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost -----+----------+-----------+---------+------+------+------+---------- 1 | 1 | -1 | -3 | -1 | 1 | 0.6 | 0 @@ -225,35 +204,6 @@ SELECT * FROM pgr_trsp_withPoints( 24 | 6 | 2 | 7 | 7 | -1 | 0 | 3 (24 rows) -/* --q5 */ -SELECT * FROM pgr_trsp_withPoints( - $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, - $$SELECT id, path, cost FROM restrictions$$, - $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - $$SELECT * FROM ( VALUES (-1, 3), (2, -3) ) AS t(source, target)$$, - driving_side => $$r$$, - details => true); - seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost ------+----------+-----------+---------+------+------+------+---------- - 1 | 1 | -1 | 3 | -1 | 1 | 0.4 | 0 - 2 | 2 | -1 | 3 | 1 | 1 | 1 | 0.4 - 3 | 3 | -1 | 3 | 2 | 4 | 0.7 | 1.4 - 4 | 4 | -1 | 3 | -6 | 4 | 0.3 | 2.1 - 5 | 5 | -1 | 3 | 5 | 10 | 1 | 2.4 - 6 | 6 | -1 | 3 | 10 | 12 | 0.6 | 3.4 - 7 | 7 | -1 | 3 | -3 | 12 | 0.4 | 4 - 8 | 8 | -1 | 3 | 11 | 13 | 1 | 4.4 - 9 | 9 | -1 | 3 | 12 | 15 | 1 | 5.4 - 10 | 10 | -1 | 3 | 9 | 16 | 1 | 6.4 - 11 | 11 | -1 | 3 | 4 | 3 | 1 | 7.4 - 12 | 12 | -1 | 3 | 3 | -1 | 0 | 8.4 - 13 | 1 | 2 | -3 | 2 | 4 | 0.7 | 0 - 14 | 2 | 2 | -3 | -6 | 4 | 0.3 | 0.7 - 15 | 3 | 2 | -3 | 5 | 10 | 1 | 1 - 16 | 4 | 2 | -3 | 10 | 12 | 0.6 | 2 - 17 | 5 | 2 | -3 | -3 | -1 | 0 | 2.6 -(17 rows) - -/* --q6 */ +/* --q3 */ ROLLBACK; ROLLBACK diff --git a/docqueries/trsp/trsp_withPoints.test.sql b/docqueries/trsp/trsp_withPoints.test.sql index 9932c761f8d..e9651abf226 100644 --- a/docqueries/trsp/trsp_withPoints.test.sql +++ b/docqueries/trsp/trsp_withPoints.test.sql @@ -5,82 +5,58 @@ SELECT * FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - -1, -3); + -1, 3, + details => true); /* --e2 */ SELECT * FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - -1, 3, - details := true); + -1, ARRAY[-3, 5]); /* --e3 */ SELECT * FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - -1, ARRAY[-3,5]); + ARRAY[-1, 2], -3); /* --e4 */ SELECT * FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - ARRAY[-1,2], -3); + ARRAY[-1, 2], ARRAY[-3, 7]); + /* --e5 */ SELECT * FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - ARRAY[-1,2], ARRAY[-3,7]); - -/* --q2 */ -SELECT ($$($$ || start_vid || $$ => $$ || end_vid ||$$) at $$ || path_seq || $$th step:$$)::TEXT AS path_at, -CASE WHEN edge = -1 THEN $$ visits$$ -ELSE $$ passes in front of$$ - END as status, - CASE WHEN node < 0 THEN $$Point$$ - ELSE $$Vertex$$ - END as is_a, - abs(node) as id - FROM pgr_trsp_withPoints( - $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, - $$SELECT id, path, cost FROM restrictions$$, - $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - ARRAY[1,-1], ARRAY[-2,-3,-6,3,6], - driving_side := $$r$$, - details := true) - WHERE node IN (-6,6); - -/* --q3 */ -SELECT ($$($$ || start_vid || $$ => $$ || end_vid ||$$) at $$ || path_seq || $$th step:$$)::TEXT AS path_at, -CASE WHEN edge = -1 THEN $$ visits$$ -ELSE $$ passes in front of$$ + $$SELECT * FROM (VALUES (-1, 3), (2, -3)) AS t(source, target)$$, + driving_side => 'r', + details => true); +/* --q1 */ +SELECT ('(' || start_vid || ' => ' || end_vid ||') at ' || path_seq || 'th step:')::TEXT AS path_at, +CASE WHEN edge = -1 THEN ' visits' +ELSE ' passes in front of' END as status, - CASE WHEN node < 0 THEN $$Point$$ - ELSE $$Vertex$$ + CASE WHEN node < 0 THEN 'Point' + ELSE 'Vertex' END as is_a, abs(node) as id FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - ARRAY[1,-1], ARRAY[-2,-3,-6,3,6], - driving_side := $$l$$, - details := true) - WHERE node IN (-6,6); -/* --q4 */ -SELECT * FROM pgr_trsp_withPoints( - $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, - $$SELECT id, path, cost FROM restrictions$$, - $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - ARRAY[-1,2], ARRAY[-3,7], - directed := false, - details := true); -/* --q5 */ + ARRAY[1,-1], ARRAY[-2, -3, -6, 3, 6], + driving_side => 'r', + details => true) + WHERE node IN (-6, 6); +/* --q2 */ SELECT * FROM pgr_trsp_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, $$SELECT id, path, cost FROM restrictions$$, $$SELECT pid, edge_id, fraction, side FROM pointsOfInterest$$, - $$SELECT * FROM ( VALUES (-1, 3), (2, -3) ) AS t(source, target)$$, - driving_side => $$r$$, + ARRAY[-1, 2], ARRAY[-3, 7], + directed => false, details => true); -/* --q6 */ +/* --q3 */ diff --git a/docqueries/withPoints/withPointsVia.result b/docqueries/withPoints/withPointsVia.result index 25fe32601e5..0c174f9c403 100644 --- a/docqueries/withPoints/withPointsVia.result +++ b/docqueries/withPoints/withPointsVia.result @@ -8,19 +8,18 @@ SET SELECT * FROM pgr_withPointsVia( 'SELECT id, source, target, cost, reverse_cost FROM edge_table order by id', 'SELECT pid, edge_id, fraction, side from pointsOfInterest', - ARRAY[-1, -3, 9], false); + ARRAY[-6, 4, -5]); seq | path_id | path_seq | start_vid | end_vid | node | edge | cost | agg_cost | route_agg_cost -----+---------+----------+-----------+---------+------+------+------+----------+---------------- - 1 | 1 | 1 | -1 | -3 | -1 | 1 | 0.6 | 0 | 0 - 2 | 1 | 2 | -1 | -3 | 2 | 4 | 1 | 0.6 | 0.6 - 3 | 1 | 3 | -1 | -3 | 5 | 10 | 1 | 1.6 | 1.6 - 4 | 1 | 4 | -1 | -3 | 10 | 12 | 0.6 | 2.6 | 2.6 - 5 | 1 | 5 | -1 | -3 | -3 | -1 | 0 | 3.2 | 3.2 - 6 | 2 | 1 | -3 | 9 | -3 | 12 | 0.4 | 0 | 3.2 - 7 | 2 | 2 | -3 | 9 | 11 | 11 | 1 | 0.4 | 3.6 - 8 | 2 | 3 | -3 | 9 | 6 | 9 | 1 | 1.4 | 4.6 - 9 | 2 | 4 | -3 | 9 | 9 | -2 | 0 | 2.4 | 5.6 -(9 rows) + 1 | 1 | 1 | -6 | 4 | -6 | 4 | 0.3 | 0 | 0 + 2 | 1 | 2 | -6 | 4 | 5 | 8 | 1 | 0.3 | 0.3 + 3 | 1 | 3 | -6 | 4 | 6 | 9 | 1 | 1.3 | 1.3 + 4 | 1 | 4 | -6 | 4 | 9 | 16 | 1 | 2.3 | 2.3 + 5 | 1 | 5 | -6 | 4 | 4 | -1 | 0 | 3.3 | 3.3 + 6 | 2 | 1 | 4 | -5 | 4 | 3 | 1 | 0 | 3.3 + 7 | 2 | 2 | 4 | -5 | 3 | 5 | 0.8 | 1 | 4.3 + 8 | 2 | 3 | 4 | -5 | -5 | -2 | 0 | 1.8 | 5.1 +(8 rows) /* -- q1 */ SELECT * FROM pgr_withPointsVia( diff --git a/docqueries/withPoints/withPointsVia.test.sql b/docqueries/withPoints/withPointsVia.test.sql index bdb4e2de3b5..8016df9ad4d 100644 --- a/docqueries/withPoints/withPointsVia.test.sql +++ b/docqueries/withPoints/withPointsVia.test.sql @@ -3,7 +3,7 @@ SET extra_float_digits=-3; SELECT * FROM pgr_withPointsVia( 'SELECT id, source, target, cost, reverse_cost FROM edge_table order by id', 'SELECT pid, edge_id, fraction, side from pointsOfInterest', - ARRAY[-1, -3, 9], false); + ARRAY[-6, 4, -5]); /* -- q1 */ SELECT * FROM pgr_withPointsVia( 'SELECT id, source, target, cost, reverse_cost FROM edge_table order by id', From 35be688bb19887bfc084dff7cce3690bb3e622cf Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 11:04:02 -0600 Subject: [PATCH 12/14] [docs][trsp][withPoints] adjusting documentation to new queries --- NEWS | 8 +- doc/conf.py.in | 8 +- doc/pickDeliver/VRP-category.rst | 3 +- doc/src/pgRouting-introduction.rst | 14 +- doc/src/proposed.rst | 1 + doc/src/release_notes.rst | 8 +- doc/src/routingFunctions.rst | 7 - doc/src/via-category.rst | 11 +- doc/trsp/TRSP-family.rst | 39 ++++- doc/trsp/pgr_trsp.rst | 30 ++-- doc/trsp/pgr_trspVia.rst | 2 +- doc/trsp/pgr_trsp_withPoints.rst | 235 ++++++++++++++++++--------- doc/withPoints/pgr_withPointsVia.rst | 22 ++- 13 files changed, 246 insertions(+), 142 deletions(-) diff --git a/NEWS b/NEWS index a25c03d75fb..a1f50ba4601 100644 --- a/NEWS +++ b/NEWS @@ -9,9 +9,10 @@ pgRouting 3.4.0 Release Notes * Turn Restrictions - * ``pgr_trspVia`` + * Via with turn restrictions * ``pgr_trspVia`` (One Via) + * ``pgr_trspVia_withPoints`` (One Via) * ``pgr_trsp`` @@ -33,9 +34,10 @@ pgRouting 3.4.0 Release Notes * Turn Restrictions - * ``pgr_trsp(text, integer, integer, boolean, boolean, text)`` - * ``pgr_trsp(text, integer, float8, integer, float8, boolean, boolean, text)`` + * ``pgr_trsp(text,integer,integer,boolean,boolean,text)`` + * ``pgr_trsp(text,integer,float8,integer,float8,boolean,boolean,text)`` * ``pgr_trspViaVertices(text,anyarray,boolean,boolean,text)`` + * ``pgr_trspViaEdges(text,integer[],float[],boolean,boolean,text)`` diff --git a/doc/conf.py.in b/doc/conf.py.in index 447c1594655..2118fe7e900 100644 --- a/doc/conf.py.in +++ b/doc/conf.py.in @@ -53,7 +53,9 @@ extensions = [ 'sphinx.ext.todo', 'sphinx.ext.mathjax', 'sphinx.ext.graphviz', + 'sphinx.ext.autosectionlabel', ] +autosectionlabel_prefix_document = True # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] @@ -318,10 +320,8 @@ linkcheck_ignore = [ 'https://docs.pgrouting.org/latest/en/pgr_trspVia.html', 'https://docs.pgrouting.org/latest/en/pgr_withPointsVia.html', 'https://docs.pgrouting.org/latest/en/via-category.html', - 'https://docs.pgrouting.org/3.4/en/pgr_withPointsVia.html', - 'https://docs.pgrouting.org/3.4/en/via-category.html', - - + 'https://docs.pgrouting.org/latest/en/pgr_trspVia_withPoints.html', + 'https://docs.pgrouting.org/3.4/en/pgr_trspVia_withPoints.html', # FIXME: tmp disable due to Retry-After header for rate-limiting by Github not respected # (see: https://github.com/sphinx-doc/sphinx/issues/7388) diff --git a/doc/pickDeliver/VRP-category.rst b/doc/pickDeliver/VRP-category.rst index e245297b87d..582c887e0dc 100644 --- a/doc/pickDeliver/VRP-category.rst +++ b/doc/pickDeliver/VRP-category.rst @@ -299,11 +299,12 @@ Results OUT service_time FLOAT, OUT departure_time FLOAT -.. _return_vrp_matrix_start: Description of the result (TODO Disussion: Euclidean & Matrix) ......................................................................................... +.. _return_vrp_matrix_start: + .. todo:: fix when everything below is fixed diff --git a/doc/src/pgRouting-introduction.rst b/doc/src/pgRouting-introduction.rst index 0fb1563308c..872797b4b9b 100644 --- a/doc/src/pgRouting-introduction.rst +++ b/doc/src/pgRouting-introduction.rst @@ -69,24 +69,23 @@ Contributors This Release Contributors +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -Individuals (in alphabetical order) +Individuals in this release (in alphabetical order) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ashish Kumar, Cayetano Benavent, Daniel Kastl, -Himanshu Raj, -Martha Vergara, +Rajat Shinde, Regina Obe, -Veenit Kumar, +Swapnil Joshi, Virginia Vergara And all the people that give us a little of their time making comments, finding issues, making pull requests etc. -in any of our products: osm2pgrouting, pgRouting, pgRoutingLayer. +in any of our products: osm2pgrouting, pgRouting, pgRoutingLayer, workshop. -Corporate Sponsors (in alphabetical order) +Corporate Sponsors in this release (in alphabetical order) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ These are corporate entities that have contributed developer time, hosting, or direct monetary funding to the pgRouting project: @@ -117,9 +116,10 @@ Jay Mahadeokar, Jinfu Leng, Kai Behncke, Kishore Kumar, Ko Nagase, Mahmoud Sakr, Manikata Kondeti, Mario Basa, Martin Wiesenhaan, Maxim Dubinin, Maoguang Wang, Mohamed Bakli, Mohamed Zia, Mukul Priya, +Rajat Shinde, Razequl Islam, Regina Obe, Rohith Reddy, -Sarthak Agarwal, Sourabh Garg, Stephen Woodbridge, Sylvain Housseman, Sylvain Pasche, +Sarthak Agarwal, Sourabh Garg, Stephen Woodbridge, Swapnil Joshi, Sylvain Housseman, Sylvain Pasche, Veenit Kumar, Vidhan Jain, Virginia Vergara Corporate Sponsors (in alphabetical order) diff --git a/doc/src/proposed.rst b/doc/src/proposed.rst index 82a9cbcb5f2..abe80f4a12e 100644 --- a/doc/src/proposed.rst +++ b/doc/src/proposed.rst @@ -126,6 +126,7 @@ Proposed Functions withPoints-family KSP-category + via-category See Also ------------------------------------------------------------------------------- diff --git a/doc/src/release_notes.rst b/doc/src/release_notes.rst index e60b4033185..9dfe3c439a7 100644 --- a/doc/src/release_notes.rst +++ b/doc/src/release_notes.rst @@ -45,9 +45,10 @@ pgRouting 3.4.0 Release Notes * Turn Restrictions - * ``pgr_trspVia`` + * Via with turn restrictions * ``pgr_trspVia`` (One Via) + * ``pgr_trspVia_withPoints`` (One Via) * ``pgr_trsp`` @@ -69,9 +70,10 @@ pgRouting 3.4.0 Release Notes * Turn Restrictions - * ``pgr_trsp(text, integer, integer, boolean, boolean, text)`` - * ``pgr_trsp(text, integer, float8, integer, float8, boolean, boolean, text)`` + * ``pgr_trsp(text,integer,integer,boolean,boolean,text)`` + * ``pgr_trsp(text,integer,float8,integer,float8,boolean,boolean,text)`` * ``pgr_trspViaVertices(text,anyarray,boolean,boolean,text)`` + * ``pgr_trspViaEdges(text,integer[],float[],boolean,boolean,text)`` diff --git a/doc/src/routingFunctions.rst b/doc/src/routingFunctions.rst index c9d4060073d..1576e4c954e 100644 --- a/doc/src/routingFunctions.rst +++ b/doc/src/routingFunctions.rst @@ -142,12 +142,6 @@ Functions by categories :start-after: index from here :end-before: index to here -:doc:`via-category` - -.. include:: via-category.rst - :start-after: from here - :end-before: to here - .. to-here .. toctree:: @@ -175,7 +169,6 @@ Functions by categories costMatrix-category drivingDistance-category spanningTree-family - via-category See Also ------------------------------------------------------------------------------- diff --git a/doc/src/via-category.rst b/doc/src/via-category.rst index 96621daf496..1634a89b340 100644 --- a/doc/src/via-category.rst +++ b/doc/src/via-category.rst @@ -16,13 +16,6 @@ Via - Category =============================================================================== -.. from here - -* :doc:`pgr_dijkstraVia` - -.. to here - - .. rubric:: proposed .. include:: proposed.rst @@ -31,8 +24,10 @@ Via - Category .. proposed start -* :doc:`pgr_trspVia` +* :doc:`pgr_dijkstraVia` * :doc:`pgr_withPointsVia` +* :doc:`pgr_trspVia` +* :doc:`pgr_trspVia_withPoints` .. proposed end diff --git a/doc/trsp/TRSP-family.rst b/doc/trsp/TRSP-family.rst index 7c453e948e6..ddfcfcbeae9 100644 --- a/doc/trsp/TRSP-family.rst +++ b/doc/trsp/TRSP-family.rst @@ -28,7 +28,9 @@ When points are also given as input: - :doc:`pgr_trsp` - Vertex - Vertex routing with restrictions. - :doc:`pgr_trspVia` - Via Vertices routing with restrictions. -- :doc:`pgr_trsp_withPoints` - Vertex/Point - Vertex/Point routing with restrictions. +- :doc:`pgr_trsp_withPoints` - Vertex/Point - Vertex/Point routing with + restrictions. +- :doc:`pgr_trspVia_withPoints` - Via Vertices/point routing with restrictions. .. index proposed to here @@ -50,18 +52,41 @@ When points are also given as input: pgr_trsp pgr_trspVia pgr_trsp_withPoints + pgr_trspVia_withPoints pgr_turnRestrictedPath Introduction ------------------------------------------------------------------------------- +Road restrictions are a sequence of road segments that can not be taken in a +sequential manner. +Some restrictions are implicit on a directed graph, for example, one way roads +where the wrong way edge is not even inserted on the graph. +But normally on turns like **left turn** or **right turn**, hence the name turn +restrictions, there are sometimes restrictions. + +.. image:: images/restrictions.png + + +| + +TRSP algorithm +............................................................................... + +The internal TRSP algorithm performs a lookahead over the dijkstra algorithm in +order to find out if the attempted path has a restriction. This allows the +algorithm to pass twice on the same vertex. + +| + Restrictions ............................................................................... On road networks, there are restrictions such as left or right turn restrictions, no U turn restrictions. -A restriction is a sequence of edges, called **path** and that **path** is to be avoided. +A restriction is a sequence of edges, called **path** and that **path** is to be +avoided. .. figure:: /images/with_restrictions.png :scale: 50% @@ -74,8 +99,11 @@ These restrictions are represented on a table as follows: :start-after: --RESTRICTIONS CREATE start :end-before: --RESTRICTIONS CREATE end -.. note:: The table has an identifier, which maybe is needed for the administration of the restrictions, but the - algorithms do not need that information. If given it will be ignored. +.. note:: The table has an identifier, which maybe is needed for the + administration of the restrictions, but the algorithms do not need that + information. If given it will be ignored. + +| Restrictions SQL +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -92,7 +120,8 @@ Restrictions SQL - Description * - ``path`` - ``ARRAY[`` **ANY-INTEGER** ``]`` - - Sequence of edge identifiers that form a path that is not allowed to be taken. + - Sequence of edge identifiers that form a path that is not allowed to be + taken. - Empty arrays or ``NULL`` arrays are ignored. - Arrays that have a ``NULL`` element will raise an exception. * - ``Cost`` diff --git a/doc/trsp/pgr_trsp.rst b/doc/trsp/pgr_trsp.rst index 952ce09891b..1aca749f5bf 100644 --- a/doc/trsp/pgr_trsp.rst +++ b/doc/trsp/pgr_trsp.rst @@ -28,7 +28,7 @@ pgr_trsp - Proposed =============================================================================== -``pgr_trsp`` Vertex - Vertex routing with restrictions. +``pgr_trsp`` - routing vertices with restrictions. .. rubric:: Availability @@ -45,6 +45,7 @@ pgr_trsp - Proposed * Signature ``pgr_trsp(text,integer,integer,boolean,boolean,text)`` is deprecated * Signature ``pgr_trsp(text,integer,float,integer,float,boolean,boolean,text)`` is deprecated * Signature ``pgr_trspViaVertices(text,anyarray,boolean,boolean,text)`` is deprecated + * Signature ``pgr_trspviaedges(text,integer[],double precision[],boolean,boolean,text)`` is deprecated * Version 2.1.0 @@ -79,7 +80,7 @@ The general algorithm is as follows: * Execute a Dijkstra * If the solution passes thru a restriction then - * Execute the TRSP algorithm with restrictions + * Execute the **TRSP** algorithm with restrictions @@ -106,14 +107,6 @@ Signatures OR EMPTY SET -.. rubric:: Prototype - -.. code-block:: none - - pgr_trspViaEdges(sql text, eids integer[], pcts float8[], - directed boolean, has_rcost boolean [, turn_restrict_sql text]) -- Prototype on v2.1 - RETURNS SETOF (seq, id1, id2, id3, cost) - .. rubric:: Deprecated .. code-block:: none @@ -126,6 +119,8 @@ Signatures pgr_trspViaVertices(sql text, vids integer[], directed boolean, has_rcost boolean [, restrictions_sql text]) -- Deprecated on v3.4 + pgr_trspViaEdges(sql text, eids integer[], pcts float8[], + directed boolean, has_rcost boolean [, restrictions_sql text]) -- Deprecated on v3.4 RETURNS SETOF (seq, id1, id2, id3, cost) .. index:: @@ -490,8 +485,8 @@ The following signature is substituted with :doc:`pgr_dijkstra` when there are n -Different ways to represent `no path found` -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +Different ways to represent `no path found` on ``pgr_trsp`` (**vertices**) +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * Sometimes represents with **EMPTY SET** a no path found * Sometimes represents with **EXCEPTION** a no path found @@ -538,7 +533,7 @@ When there are restrictions the proposed ``pgr_trsp`` (`One to One`_) should be :start-after: --place8 :end-before: --place9 -User contradictions +User contradictions ``pgr_trsp`` (**vertices**) +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ``pgr_trsp`` unlike other pgRouting functions does not autodetect the existence of ``reverse_cost`` column. Therefore it @@ -588,7 +583,7 @@ The "Edges" signature version target_edge integer, target_pos float8, directed boolean, has_rcost boolean [,restrict_sql text]); -Different ways to represent `no path found` +Different ways to represent `no path found ``pgr_trsp`` (**edges**) +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * Sometimes represents with **EMPTY SET** a no path found @@ -691,11 +686,12 @@ When there are restrictions :doc:`pgr_trsp_withPoints` (One to One) should be us :start-after: --place16.2 :end-before: --place17 -User contradictions +User contradictions ``pgr_trsp`` (**edges**) +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -``pgr_trsp`` unlike other pgRouting functions does not autodetect the existence of ``reverse_cost`` column. Therefore it -has ``has_rcost`` parameter to check the existence of ``reverse_cost`` column. Contradictions happen: +``pgr_trsp`` unlike other pgRouting functions does not autodetect the existence +of ``reverse_cost`` column. Therefore it has ``has_rcost`` parameter to check +the existence of ``reverse_cost`` column. Contradictions happen: - When the ``reverse_cost`` is missing, and the flag ``has_rcost`` is set to true - When the ``reverse_cost`` exists, and the flag ``has_rcost`` is set to false diff --git a/doc/trsp/pgr_trspVia.rst b/doc/trsp/pgr_trspVia.rst index f5c5abd0cd7..553e4c1aa7b 100644 --- a/doc/trsp/pgr_trspVia.rst +++ b/doc/trsp/pgr_trspVia.rst @@ -49,7 +49,7 @@ The general algorithm is as follows: * Execute a :doc:`pgr_dijkstraVia`. * For the set of sub paths of the solution that pass through a restriction then - * Execute the :doc:`pgr_trsp` algorithm with restrictions for the sub paths. + * Execute the **TRSP** algorithm with restrictions for the paths. * **NOTE** when this is done, ``U_turn_on_edge`` flag is ignored. diff --git a/doc/trsp/pgr_trsp_withPoints.rst b/doc/trsp/pgr_trsp_withPoints.rst index cc3a1297ede..3c1ea7be49f 100644 --- a/doc/trsp/pgr_trsp_withPoints.rst +++ b/doc/trsp/pgr_trsp_withPoints.rst @@ -16,7 +16,7 @@ pgr_trsp_withPoints - Proposed =============================================================================== -``pgr_trsp_withPoints`` Vertex/Point - Vertex/Point routing with restrictions. +``pgr_trsp_withPoints`` Routing Vertex/Point with restrictions. .. include:: proposed.rst :start-after: begin-warning @@ -48,6 +48,7 @@ Using Dijkstra algorithm, find the shortest path(s) **The main characteristics are:** - Process is done only on edges with positive costs. +- Driving side can not be `b` (**both**) - Vertices of the graph are: - **positive** when it belongs to the `Edges SQL`_ @@ -59,11 +60,13 @@ Using Dijkstra algorithm, find the shortest path(s) - The agg_cost the non included values (v, v) is 0 - - When the starting vertex and ending vertex are the different and there is no path: + - When the starting vertex and ending vertex are the different and there is no + path: - The agg_cost the non included values (u, v) is ∞ -- For optimization purposes, any duplicated value in the start_vids or end_vids are ignored. +- For optimization purposes, any duplicated value in the start_vids or end_vids + are ignored. - The returned values are ordered: - start_vid ascending - end_vid ascending @@ -98,16 +101,17 @@ One to One .. parsed-literal:: - pgr_trsp_withPoints(`Edges SQL`_, `Restrictions SQL`_, `Points SQL`_, start vid, end vids + pgr_trsp_withPoints(`Edges SQL`_, `Restrictions SQL`_, `Points SQL`_, start vid, end vid [, directed], [, driving_side] [, details]) -- Proposed on v3.4 RETURNS SET OF (seq, path_seq, start_vid, end_vid, node, edge, cost, agg_cost) OR EMPTY SET -:Example: From point :math:`1` to vertex :math:`3` with details of passing points +:Example: From point :math:`1` to vertex :math:`3` with details on a **left** + driving side configuration on a **directed** graph with **details**. .. literalinclude:: trsp_withPoints.queries - :start-after: --e2 - :end-before: --e3 + :start-after: --e1 + :end-before: --e2 .. index:: single: trsp_withPoints(One to Many) - Proposed on v3.4 @@ -122,11 +126,11 @@ One to Many RETURNS SET OF (seq, path_seq, start_vid, end_vid, node, edge, cost, agg_cost) OR EMPTY SET -:Example: From point :math:`1` to point :math:`3` and vertex :math:`5` +:Example: From point :math:`1` to point :math:`3` and vertex :math:`5`. .. literalinclude:: trsp_withPoints.queries - :start-after: --e3 - :end-before: --e4 + :start-after: --e2 + :end-before: --e3 .. index:: single: trsp_withPoints(Many to One) - Proposed on v3.4 @@ -141,11 +145,11 @@ Many to One RETURNS SET OF (seq, path_seq, start_vid, end_vid, node, edge, cost, agg_cost) OR EMPTY SET -:Example: From point :math:`1` and vertex :math:`2` to point :math:`3` +:Example: From point :math:`1` and vertex :math:`2` to point :math:`3`. .. literalinclude:: trsp_withPoints.queries - :start-after: --e4 - :end-before: --e5 + :start-after: --e3 + :end-before: --e4 .. index:: single: trsp_withPoints(Many to Many) - Proposed on v3.4 @@ -160,11 +164,12 @@ Many to Many RETURNS SET OF (seq, path_seq, start_vid, end_vid, node, edge, cost, agg_cost) OR EMPTY SET -:Example: From point :math:`1` and vertex :math:`2` to point :math:`3` and vertex :math:`7` +:Example: From point :math:`1` and vertex :math:`2` to point :math:`3` and + vertex :math:`7`. .. literalinclude:: trsp_withPoints.queries - :start-after: --e5 - :end-before: --q2 + :start-after: --e4 + :end-before: --e5 .. index:: single: trsp_withPoints(Combinations) - Proposed on v3.4 @@ -178,38 +183,93 @@ Combinations [, directed], [, driving_side] [, details]) -- Proposed on v3.4 RETURNS SET OF (seq, path_seq, start_vid, end_vid, node, edge, cost, agg_cost) -:Example: Two (source, target) combinations: (from point :math:`1` to vertex :math:`3`), and (from vertex :math:`2` to - point :math:`3`) with **right** side driving topology. - +:Example: From point :math:`1` to vertex :math:`3` and from vertex :math:`2` to + point :math:`3` with **right** side driving configuration. .. literalinclude:: trsp_withPoints.queries - :start-after: --q5 - :end-before: --q6 + :start-after: --e5 + :end-before: --q1 Parameters ------------------------------------------------------------------------------- -====================== ================================== ========= ========================================== -Parameter Type Default Description -====================== ================================== ========= ========================================== -`Edges SQL`_ ``TEXT`` `Edges SQL`_ as described below. -`Points SQL`_ ``TEXT`` `Points SQL`_ as described below. -`Combinations SQL`_ ``TEXT`` `Combinations SQL`_ as described below. -**start vid** **ANY-INTEGER** Starting vertex identifier. When negative: is a point's identifier. -**end vid** **ANY-INTEGER** Ending vertex identifier. When negative: is a point's identifier. -**start vids** ``ARRAY[`` **ANY-INTEGER** ``]`` Array of identifiers of starting vertices. When negative: is a point's identifier. -**end vids** ``ARRAY[`` **ANY-INTEGER]** ``]`` Array of identifiers of ending vertices. When negative: is a point's identifier. -``directed`` ``BOOLEAN`` ``true`` - When ``true`` the graph is considered as directed. - - When ``false`` the graph is considred as undirected. - -``driving_side`` ``CHAR`` ``b`` Value in :math:`{b,r,l}` indicating if the driving side is: - - ``r`` for right driving side - - ``l`` for left driving side - - ``b`` for both, or ignore driving side - -``details`` ``BOOLEAN`` ``false`` - When ``true`` the results will include the points in `Points SQL`_ that are in the path. - - When ``false`` ignores other points of the `Points SQL`_. -====================== ================================== ========= ========================================== +.. parameters_start + +.. list-table:: + :width: 81 + :widths: 14 22 7 44 + :header-rows: 1 + + * - Column + - Type + - Default + - Description + * - `Edges SQL`_ + - ``TEXT`` + - + - SQL query as described. + * - `Restrictions SQL`_ + - ``TEXT`` + - + - SQL query as described. + * - `Points SQL`_ + - ``TEXT`` + - + - SQL query as described. + * - **start vid** + - **ANY-INTEGER** + - + - Identifier of the departure vertex. + * - **start vids** + - ``ARRAY[`` **ANY-INTEGER** ``]`` + - + - Array of identifieris of destination vertices. + * - **end vid** + - **ANY-INTEGER** + - + - Identifier of the departure vertex. + * - **end vids** + - ``ARRAY[`` **ANY-INTEGER** ``]`` + - + - Array of identifiers of destination vertices. + * - ``directed`` + - ``BOOLEAN`` + - ``true`` + - - When ``true`` Graph is considered `Directed` + - When ``false`` the graph is considered as Undirected. + +With points optional parameters +............................................................................... + +.. withPoints_parameters_start + +.. list-table:: + :width: 81 + :widths: 14 7 7 60 + :header-rows: 1 + + * - Parameter + - Type + - Default + - Description + * - ``driving_side`` + - ``CHAR`` + - ``r`` + - Value in [``r``, ``l``] indicating if the driving side is: + + - ``r`` for right driving side + - ``l`` for left driving side + - Any other value will be considered as ``r`` + * - ``details`` + - ``BOOLEAN`` + - ``false`` + - - When ``true`` the results will include the points that are in the path. + - When ``false`` the results will not include the points that are in the + path. + +.. withPoints_parameters_end + +| Inner query ------------------------------------------------------------------------------- @@ -242,62 +302,79 @@ Combinations SQL :start-after: basic_combinations_sql_start :end-before: basic_combinations_sql_end -Result Columns +Return Columns ------------------------------------------------------------------------------- -============= =========== ================================================= -Column Type Description -============= =========== ================================================= -``seq`` ``INTEGER`` Row sequence. -``path_seq`` ``INTEGER`` Path sequence that indicates the relative position on the path. -``start_vid`` ``BIGINT`` Identifier of the starting vertex. When negative: is a point's identifier. -``end_vid`` ``BIGINT`` Identifier of the ending vertex. When negative: is a point's identifier. -``node`` ``BIGINT`` Identifier of the node: - - A positive value indicates the node is a vertex from `Edges SQL`_. - - A negative value indicates the node is a point from `Points SQL`_. +.. list-table:: + :width: 81 + :widths: 12 14 60 + :header-rows: 1 + + * - Column + - Type + - Description + * - ``seq`` + - ``INTEGER`` + - Sequential value starting from **1**. + * - ``path_seq`` + - ``INTEGER`` + - Relative position in the path. Has value **1** for the beginning of a + path. + * - ``start_vid`` + - ``BIGINT`` + - Identifier of the starting vertex of the path. + * - ``end_vid`` + - ``BIGINT`` + - Identifier of the ending vertex of the path. + * - ``node`` + - ``BIGINT`` + - Identifier of the node in the path from ``start_vid`` to ``end_vid``. + * - ``edge`` + - ``BIGINT`` + - Identifier of the edge used to go from ``node`` to the next node in the + path sequence. + + * -1 for the last node of the path. + * - ``cost`` + - ``FLOAT`` + - Cost to traverse from ``node`` using ``edge`` to the next node in the + path sequence. + + - ``0`` for the last row in the path sequence. + * - ``agg_cost`` + - ``FLOAT`` + - Aggregate cost from ``start_vid`` to ``node``. + + - ``0`` for the first row in the path sequence. + +.. result columns end -``edge`` ``BIGINT`` Identifier of the edge used to go from ``node`` to the next node in the path sequence. - - ``-1`` for the last row in the path sequence. - -``cost`` ``FLOAT`` Cost to traverse from ``node`` using ``edge`` to the next ``node`` in the path sequence. - - ``0`` for the last row in the path sequence. - -``agg_cost`` ``FLOAT`` Aggregate cost from ``start_pid`` to ``node``. - - ``0`` for the first row in the path sequence. - -============= =========== ================================================= +| Additional Examples ------------------------------------------------------------------------------- -:Example: Which path (if any) passes in front of point :math:`6` or vertex :math:`6` with **right** side driving - topology. +:Example: Which path (if any) passes in front of point :math:`6` or vertex + :math:`6` with **right** side driving topology. .. literalinclude:: trsp_withPoints.queries - :start-after: --q2 - :end-before: --q3 - -:Example: Which path (if any) passes in front of point :math:`6` or vertex :math:`6` with **left** side driving - topology. + :start-after: --q1 + :end-before: --q2 -.. literalinclude:: trsp_withPoints.queries - :start-after: --q3 - :end-before: --q4 -:Example: From point :math:`1` and vertex :math:`2` to point :math:`3` to vertex :math:`7` on an **undirected** graph, - with details. +:Example: From point :math:`1` and vertex :math:`2` to point :math:`3` to vertex + :math:`7` on an **undirected** graph, with details. .. literalinclude:: trsp_withPoints.queries - :start-after: --q4 - :end-before: --q5 - -The queries use the :doc:`sampledata` network + :start-after: --q2 + :end-before: --q3 See Also ------------------------------------------------------------------------------- * :doc:`TRSP-family` * :doc:`withPoints-family` +* :doc:`sampledata` .. rubric:: Indices and tables diff --git a/doc/withPoints/pgr_withPointsVia.rst b/doc/withPoints/pgr_withPointsVia.rst index 71d02d42eae..a3fb20dd27d 100644 --- a/doc/withPoints/pgr_withPointsVia.rst +++ b/doc/withPoints/pgr_withPointsVia.rst @@ -39,17 +39,25 @@ Description ------------------------------------------------------------------------------- -Given a list of vertices and a graph, this function is equivalent to finding the -shortest path between :math:`vertex_i` and :math:`vertex_{i+1}` for all :math:`i -< size\_of(via\;vertices)` when vertices can be points. +Given a graph, a set of points on the graphs edges and a list of vertices, this +function is equivalent to finding the shortest path between :math:`vertex_i` and +:math:`vertex_{i+1}` (where :math:`vertex` can be a vertex or a point on the +graph) for all :math:`i < size\_of(via\;vertices)`. -The paths represents the sections of the route. +:Route: is a sequence of paths +:Path: is a section of the route. The general algorithm is as follows: -* Add the additional points to the graph +* Build the Graph with the new points. + + * The points identifiers will be converted to negative values. + * The vertices identifiers will remain positive. + * Execute a :doc:`pgr_dijkstraVia`. +.. Note:: Do not use negative values on identifiers of the inner queries. + | Signatures @@ -81,8 +89,8 @@ One Via node, edge, cost, agg_cost, route_agg_cost) OR EMPTY SET -:Example: Find the route that visits the vertices :math:`\{ -1, -3, 9\}` in that - order on an **undirected** graph. +:Example: Find the route that visits the vertices :math:`\{ -6, 4, -5\}` in that + order on a **directed** graph. .. literalinclude:: withPointsVia.queries :start-after: -- q0 From 43a27e0719897ec800922de549c1fbab6c9eccd0 Mon Sep 17 00:00:00 2001 From: cvvergara Date: Mon, 7 Mar 2022 13:25:27 -0600 Subject: [PATCH 13/14] [issue][2254] pgtap test --- .../withPoints/edge_cases/issue_2254.pg | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 pgtap/withPoints/withPoints/edge_cases/issue_2254.pg diff --git a/pgtap/withPoints/withPoints/edge_cases/issue_2254.pg b/pgtap/withPoints/withPoints/edge_cases/issue_2254.pg new file mode 100644 index 00000000000..e30e215d67a --- /dev/null +++ b/pgtap/withPoints/withPoints/edge_cases/issue_2254.pg @@ -0,0 +1,37 @@ +BEGIN; + +SELECT plan(5); + +CREATE TABLE e_table (id integer, source integer, target integer, cost float); +INSERT INTO e_table VALUES (1, 10, 11, 10.0); + +CREATE TABLE poi_table (pid integer, edge_id integer, fraction float); +INSERT INTO poi_table VALUES (20, 1, 0.6); + +PREPARE q1 AS +SELECT * FROM pgr_withPoints( +'SELECT id, source, target, cost FROM e_table', +'SELECT pid, edge_id, fraction FROM poi_table', + 10, + 11, + false); + +SELECT * FROM lives_ok('q1'); +SELECT * FROM isnt_empty('q1'); + +PREPARE q2 AS +SELECT * +FROM pgr_withPoints( +'SELECT id, source, target, cost FROM e_table', +'SELECT pid, edge_id, fraction FROM poi_table WHERE pid != 20', + 10, + 11, + false); + +SELECT * FROM lives_ok('q2'); +SELECT * FROM isnt_empty('q2'); + +SELECT * FROM set_eq('q1','q2'); + +SELECT finish(); +ROLLBACK; From 9aa0ffd9cabbad7dcbb64acd4798d568bb3a293d Mon Sep 17 00:00:00 2001 From: cvvergara Date: Wed, 9 Mar 2022 10:33:29 -0600 Subject: [PATCH 14/14] fixing issues found --- doc/trsp/pgr_trspVia_withPoints.rst | 2 +- docqueries/trsp/trspVia_withPoints.result | 47 --------------------- docqueries/trsp/trspVia_withPoints.test.sql | 16 ------- src/trsp/trspVia_withPoints_driver.cpp | 2 +- 4 files changed, 2 insertions(+), 65 deletions(-) diff --git a/doc/trsp/pgr_trspVia_withPoints.rst b/doc/trsp/pgr_trspVia_withPoints.rst index 4ca734aa0d7..8f67ac8b50b 100644 --- a/doc/trsp/pgr_trspVia_withPoints.rst +++ b/doc/trsp/pgr_trspVia_withPoints.rst @@ -93,7 +93,7 @@ One Via node, edge, cost, agg_cost, route_agg_cost) OR EMPTY SET -:Example: Find the route that visits the vertices :math:`\{ 1, 7, 10\}` in that +:Example: Find the route that visits the vertices :math:`\{ -6, 4, -5\}` in that order on an **directed** graph. .. literalinclude:: trspVia_withPoints.queries diff --git a/docqueries/trsp/trspVia_withPoints.result b/docqueries/trsp/trspVia_withPoints.result index 82814d1e8ca..9754da0eb67 100644 --- a/docqueries/trsp/trspVia_withPoints.result +++ b/docqueries/trsp/trspVia_withPoints.result @@ -24,53 +24,6 @@ SELECT * FROM pgr_trspVia_withPoints( 10 | 2 | 3 | 4 | -5 | -5 | -2 | 0 | 1.8 | 7.1 (10 rows) -SELECT * FROM pgr_trsp_withPoints( - $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, - $$SELECT path, cost FROM restrictions$$, - $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, - -6, 4); - seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost ------+----------+-----------+---------+------+------+------+---------- - 1 | 1 | -6 | 4 | -6 | 4 | 0.3 | 0 - 2 | 2 | -6 | 4 | 5 | 10 | 1 | 0.3 - 3 | 3 | -6 | 4 | 10 | 12 | 1 | 1.3 - 4 | 4 | -6 | 4 | 11 | 13 | 1 | 2.3 - 5 | 5 | -6 | 4 | 12 | 15 | 1 | 3.3 - 6 | 6 | -6 | 4 | 9 | 16 | 1 | 4.3 - 7 | 7 | -6 | 4 | 4 | -1 | 0 | 5.3 -(7 rows) - -SELECT * FROM pgr_trsp_withPoints( - $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, - $$SELECT path, cost FROM restrictions$$, - $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, - 2, 4); - seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost ------+----------+-----------+---------+------+------+------+---------- - 1 | 1 | 2 | 4 | 2 | 4 | 1 | 0 - 2 | 2 | 2 | 4 | 5 | 10 | 1 | 1 - 3 | 3 | 2 | 4 | 10 | 12 | 1 | 2 - 4 | 4 | 2 | 4 | 11 | 13 | 1 | 3 - 5 | 5 | 2 | 4 | 12 | 15 | 1 | 4 - 6 | 6 | 2 | 4 | 9 | 16 | 1 | 5 - 7 | 7 | 2 | 4 | 4 | -1 | 0 | 6 -(7 rows) - -SELECT * FROM pgr_trsp_withPoints( - $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, - $$SELECT path, cost FROM restrictions$$, - $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, - 5, 4); - seq | path_seq | start_vid | end_vid | node | edge | cost | agg_cost ------+----------+-----------+---------+------+------+------+---------- - 1 | 1 | 5 | 4 | 5 | 10 | 1 | 0 - 2 | 2 | 5 | 4 | 10 | 12 | 1 | 1 - 3 | 3 | 5 | 4 | 11 | 13 | 1 | 2 - 4 | 4 | 5 | 4 | 12 | 15 | 1 | 3 - 5 | 5 | 5 | 4 | 9 | 16 | 1 | 4 - 6 | 6 | 5 | 4 | 4 | -1 | 0 | 5 -(6 rows) - /* -- q1 */ SELECT * FROM pgr_trspVia_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, diff --git a/docqueries/trsp/trspVia_withPoints.test.sql b/docqueries/trsp/trspVia_withPoints.test.sql index dc592f6209b..7b7ade8d3d9 100644 --- a/docqueries/trsp/trspVia_withPoints.test.sql +++ b/docqueries/trsp/trspVia_withPoints.test.sql @@ -6,22 +6,6 @@ SELECT * FROM pgr_trspVia_withPoints( $$SELECT path, cost FROM restrictions$$, $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, ARRAY[-6, 4, -5]); -SELECT * FROM pgr_trsp_withPoints( - $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, - $$SELECT path, cost FROM restrictions$$, - $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, - -6, 4); -SELECT * FROM pgr_trsp_withPoints( - $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, - $$SELECT path, cost FROM restrictions$$, - $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, - 2, 4); -SELECT * FROM pgr_trsp_withPoints( - $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, - $$SELECT path, cost FROM restrictions$$, - $$SELECT pid, edge_id, side, fraction FROM pointsOfInterest$$, - 5, 4); - /* -- q1 */ SELECT * FROM pgr_trspVia_withPoints( $$SELECT id, source, target, cost, reverse_cost FROM edge_table ORDER BY id$$, diff --git a/src/trsp/trspVia_withPoints_driver.cpp b/src/trsp/trspVia_withPoints_driver.cpp index c65b96ebc40..5e1574ee726 100644 --- a/src/trsp/trspVia_withPoints_driver.cpp +++ b/src/trsp/trspVia_withPoints_driver.cpp @@ -151,7 +151,7 @@ do_trspVia_withPoints( std::ostringstream notice; try { - pgassert(total_edges != 0); + pgassert((total_edges + total_edges_of_points) != 0); pgassert(!(*log_msg)); pgassert(!(*notice_msg)); pgassert(!(*err_msg));