From a934c460e4d9ced15d6927ac1d3fec8225924972 Mon Sep 17 00:00:00 2001 From: Petros Koutsolampros Date: Tue, 29 Dec 2020 15:42:16 +0200 Subject: [PATCH 1/4] VGA Shortest paths first commit --- depthmapX/mainwindowmoduleregistry.cpp | 2 + depthmapXcli/modeparserregistry.cpp | 4 + modules/vgapaths/CMakeLists.txt | 34 +++ modules/vgapaths/cli/CMakeLists.txt | 25 +++ modules/vgapaths/cli/isovistzoneparser.cpp | 117 ++++++++++ modules/vgapaths/cli/isovistzoneparser.h | 48 +++++ modules/vgapaths/cli/shortestpathparser.cpp | 153 +++++++++++++ modules/vgapaths/cli/shortestpathparser.h | 49 +++++ modules/vgapaths/cliTest/CMakeLists.txt | 24 +++ .../cliTest/testshortestpathparser.cpp | 85 ++++++++ modules/vgapaths/core/CMakeLists.txt | 37 ++++ modules/vgapaths/core/extractlinkdata.cpp | 45 ++++ modules/vgapaths/core/extractlinkdata.h | 34 +++ .../vgapaths/core/vgaangularshortestpath.cpp | 155 ++++++++++++++ .../vgapaths/core/vgaangularshortestpath.h | 36 ++++ modules/vgapaths/core/vgaisovistzone.cpp | 123 +++++++++++ modules/vgapaths/core/vgaisovistzone.h | 44 ++++ .../vgapaths/core/vgametricdepthlinkcost.cpp | 108 ++++++++++ .../vgapaths/core/vgametricdepthlinkcost.h | 47 ++++ .../vgapaths/core/vgametricshortestpath.cpp | 178 ++++++++++++++++ modules/vgapaths/core/vgametricshortestpath.h | 50 +++++ .../core/vgametricshortestpathtomany.cpp | 190 +++++++++++++++++ .../core/vgametricshortestpathtomany.h | 50 +++++ .../vgapaths/core/vgavisualshortestpath.cpp | 172 +++++++++++++++ modules/vgapaths/core/vgavisualshortestpath.h | 40 ++++ modules/vgapaths/coreTest/CMakeLists.txt | 24 +++ .../vgapaths/coreTest/vgapathscoretest.cpp | 18 ++ modules/vgapaths/gui/CMakeLists.txt | 39 ++++ modules/vgapaths/gui/vgapathsmainwindow.cpp | 201 ++++++++++++++++++ modules/vgapaths/gui/vgapathsmainwindow.h | 34 +++ salalib/entityparsing.cpp | 85 ++++++++ salalib/entityparsing.h | 1 + 32 files changed, 2252 insertions(+) create mode 100644 modules/vgapaths/CMakeLists.txt create mode 100644 modules/vgapaths/cli/CMakeLists.txt create mode 100644 modules/vgapaths/cli/isovistzoneparser.cpp create mode 100644 modules/vgapaths/cli/isovistzoneparser.h create mode 100644 modules/vgapaths/cli/shortestpathparser.cpp create mode 100644 modules/vgapaths/cli/shortestpathparser.h create mode 100644 modules/vgapaths/cliTest/CMakeLists.txt create mode 100644 modules/vgapaths/cliTest/testshortestpathparser.cpp create mode 100644 modules/vgapaths/core/CMakeLists.txt create mode 100644 modules/vgapaths/core/extractlinkdata.cpp create mode 100644 modules/vgapaths/core/extractlinkdata.h create mode 100644 modules/vgapaths/core/vgaangularshortestpath.cpp create mode 100644 modules/vgapaths/core/vgaangularshortestpath.h create mode 100644 modules/vgapaths/core/vgaisovistzone.cpp create mode 100644 modules/vgapaths/core/vgaisovistzone.h create mode 100644 modules/vgapaths/core/vgametricdepthlinkcost.cpp create mode 100644 modules/vgapaths/core/vgametricdepthlinkcost.h create mode 100644 modules/vgapaths/core/vgametricshortestpath.cpp create mode 100644 modules/vgapaths/core/vgametricshortestpath.h create mode 100644 modules/vgapaths/core/vgametricshortestpathtomany.cpp create mode 100644 modules/vgapaths/core/vgametricshortestpathtomany.h create mode 100644 modules/vgapaths/core/vgavisualshortestpath.cpp create mode 100644 modules/vgapaths/core/vgavisualshortestpath.h create mode 100644 modules/vgapaths/coreTest/CMakeLists.txt create mode 100644 modules/vgapaths/coreTest/vgapathscoretest.cpp create mode 100644 modules/vgapaths/gui/CMakeLists.txt create mode 100644 modules/vgapaths/gui/vgapathsmainwindow.cpp create mode 100644 modules/vgapaths/gui/vgapathsmainwindow.h diff --git a/depthmapX/mainwindowmoduleregistry.cpp b/depthmapX/mainwindowmoduleregistry.cpp index 5238510f..0c22a327 100644 --- a/depthmapX/mainwindowmoduleregistry.cpp +++ b/depthmapX/mainwindowmoduleregistry.cpp @@ -16,9 +16,11 @@ #include "mainwindowmoduleregistry.hpp" #include "modules/segmentshortestpaths/gui/segmentpathsmainwindow.h" +#include "modules/vgapaths/gui/vgapathsmainwindow.h" void MainWindowModuleRegistry::populateModules() { // Register any main window modules here REGISTER_MAIN_WINDOW_MODULE(SegmentPathsMainWindow); + REGISTER_MAIN_WINDOW_MODULE(VGAPathsMainWindow); // ********* } diff --git a/depthmapXcli/modeparserregistry.cpp b/depthmapXcli/modeparserregistry.cpp index e54a3d4f..2187a641 100644 --- a/depthmapXcli/modeparserregistry.cpp +++ b/depthmapXcli/modeparserregistry.cpp @@ -26,6 +26,8 @@ #include "stepdepthparser.h" #include "mapconvertparser.h" #include "modules/segmentshortestpaths/cli/segmentshortestpathparser.h" +#include "modules/vgapaths/cli/shortestpathparser.h" +#include "modules/vgapaths/cli/isovistzoneparser.h" void ModeParserRegistry::populateParsers() @@ -43,5 +45,7 @@ void ModeParserRegistry::populateParsers() REGISTER_PARSER(StepDepthParser); REGISTER_PARSER(MapConvertParser); REGISTER_PARSER(SegmentShortestPathParser); + REGISTER_PARSER(ShortestPathParser); + REGISTER_PARSER(IsovistZoneParser); // ********* } diff --git a/modules/vgapaths/CMakeLists.txt b/modules/vgapaths/CMakeLists.txt new file mode 100644 index 00000000..70b4dd7b --- /dev/null +++ b/modules/vgapaths/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# 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 3 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, see . + +if(MODULES_CORE) +add_subdirectory(core) +endif() + +if(MODULES_GUI) + add_subdirectory(gui) +endif() + +if(MODULES_CLI) + add_subdirectory(cli) +endif() + +if(MODULES_CORE_TEST) + add_subdirectory(coreTest) +endif() + +if(MODULES_CLI_TEST) + add_subdirectory(cliTest) +endif() diff --git a/modules/vgapaths/cli/CMakeLists.txt b/modules/vgapaths/cli/CMakeLists.txt new file mode 100644 index 00000000..451a34e9 --- /dev/null +++ b/modules/vgapaths/cli/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# 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 3 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, see . + +set(vgapathscli vgapathscli) +set(vgapathscli_SRCS + shortestpathparser.cpp + isovistzoneparser.cpp) + +set(modules_cli "${modules_cli}" "vgapathscli" CACHE INTERNAL "modules_cli" FORCE) + +add_compile_definitions(VGAPATHS_CLI_LIBRARY) + +add_library(${vgapathscli} OBJECT ${vgapathscli_SRCS}) diff --git a/modules/vgapaths/cli/isovistzoneparser.cpp b/modules/vgapaths/cli/isovistzoneparser.cpp new file mode 100644 index 00000000..ee2bd889 --- /dev/null +++ b/modules/vgapaths/cli/isovistzoneparser.cpp @@ -0,0 +1,117 @@ +// Copyright (C) 2019 Petros Koutsolampros + +// 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 3 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, see . + +#include "isovistzoneparser.h" +#include "depthmapXcli/exceptions.h" +#include "depthmapXcli/parsingutils.h" +#include "depthmapXcli/runmethods.h" +#include "depthmapXcli/simpletimer.h" +#include "modules/vgapaths/core/vgaisovistzone.h" +#include "salalib/entityparsing.h" +#include +#include + +using namespace depthmapX; + +void IsovistZoneParser::parse(int argc, char **argv) { + + std::vector origins; + std::string originsFile; + for (int i = 1; i < argc; ++i) { + if (std::strcmp("-izo", argv[i]) == 0) { + ENFORCE_ARGUMENT("-izo", i) + if (!has_only_digits_dots_commas(argv[i])) { + std::stringstream message; + message << "Invalid origin point provided (" << argv[i] + << "). Should only contain digits dots and commas" << std::flush; + throw CommandLineException(message.str().c_str()); + } + origins.push_back(argv[i]); + } else if (std::strcmp("-izr", argv[i]) == 0) { + ENFORCE_ARGUMENT("-izr", i) + if (!has_only_digits_dots_commas(argv[i])) { + std::stringstream message; + message << "Invalid restriction distance provided (" << argv[i] + << "). Should only contain digits dots and commas" << std::flush; + throw CommandLineException(message.str().c_str()); + } + m_restrictDistance = std::stof(argv[i]); + } else if (std::strcmp("-izf", argv[i]) == 0) { + ENFORCE_ARGUMENT("-izf", i) + originsFile = argv[i]; + } else if (std::strcmp("-izn", argv[i]) == 0) { + ENFORCE_ARGUMENT("-izn", i) + m_originSets.push_back(argv[i]); + } + } + + if (!originsFile.empty()) { + std::ifstream file(originsFile); + if (!file.good()) { + std::stringstream message; + message << "Failed to find file " << originsFile; + throw depthmapX::CommandLineException(message.str()); + } + auto pointSets = EntityParsing::parsePointSets(file, ','); + m_originSets = pointSets.first; + m_origins = pointSets.second; + } else if (origins.empty()) { + throw CommandLineException("At least one origin point (-izo) or a file (-izf) must be provided"); + } else if (m_originSets.size() > 0 & origins.size() != m_originSets.size()) { + throw CommandLineException("Origin sets must either not be provided or be provided for every origin"); + } else { + for (const auto &origin : origins) { + m_origins.push_back(EntityParsing::parsePoint(origin)); + } + if (m_originSets.size() == 0) { + for (const auto &origin : origins) { + m_originSets.push_back(""); + } + } + } +} + +void IsovistZoneParser::run(const CommandLineParser &clp, IPerformanceSink &perfWriter) const { + auto mGraph = dm_runmethods::loadGraph(clp.getFileName().c_str(), perfWriter); + + auto &graphRegion = mGraph->getRegion(); + PointMap &map = mGraph->getDisplayedPointMap(); + + std::map> pixelsFrom; + auto namesIter = m_originSets.begin(); + for (const Point2f &origin : m_origins) { + if (!graphRegion.contains(origin) || !map.getRegion().contains(origin)) { + throw depthmapX::RuntimeException("Origin point " + std::to_string(origin.x) + ", " + + std::to_string(origin.y) + " outside of target region"); + } + PixelRef pixelFrom = map.pixelate(origin); + if (!map.getPoint(pixelFrom).filled()) { + throw depthmapX::RuntimeException("Origin point " + std::to_string(origin.x) + ", " + + std::to_string(origin.y) + " not filled in target pointmap"); + } + pixelsFrom[*namesIter].insert(pixelFrom); + namesIter++; + } + + std::cout << "ok\nCalculating isovist zone... " << std::flush; + + std::unique_ptr comm(new ICommunicator()); + + DO_TIMED("Calculating isovist zone", VGAIsovistZone(map, pixelsFrom, m_restrictDistance).run(comm.get())); + + std::cout << " ok\nWriting out result..." << std::flush; + DO_TIMED("Writing graph", mGraph->write(clp.getOuputFile().c_str(), METAGRAPH_VERSION, false)) + std::cout << " ok" << std::endl; +} diff --git a/modules/vgapaths/cli/isovistzoneparser.h b/modules/vgapaths/cli/isovistzoneparser.h new file mode 100644 index 00000000..c00c6a61 --- /dev/null +++ b/modules/vgapaths/cli/isovistzoneparser.h @@ -0,0 +1,48 @@ +// Copyright (C) 2017 Petros Koutsolampros + +// 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 3 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, see . + +#pragma once + +#include "depthmapXcli/imodeparser.h" +#include "genlib/p2dpoly.h" +#include + +class IsovistZoneParser : public IModeParser { + public: + IsovistZoneParser() {} + + virtual std::string getModeName() const { return "ISOVISTZONE"; } + + virtual std::string getHelp() const { + return "Mode options for pointmap ISOVISTZONE are:\n" + " -izo Origin point (can be given multiple times)\n" + " -izn Set of an origin. Either not provided or provided for every origin\n" + " -izf load origins from a file (csv)\n" + " the relevant headers must be called x, y and set\n" + " the last one is optional.\n" + " -izr Restrict distance of isovist zone from origins\n"; + } + + virtual void parse(int argc, char **argv); + + virtual void run(const CommandLineParser &clp, IPerformanceSink &perfWriter) const; + + std::vector getOrigins() const { return m_origins; } + + private: + std::vector m_origins; + std::vector m_originSets; + float m_restrictDistance = -1; +}; diff --git a/modules/vgapaths/cli/shortestpathparser.cpp b/modules/vgapaths/cli/shortestpathparser.cpp new file mode 100644 index 00000000..54a16fe3 --- /dev/null +++ b/modules/vgapaths/cli/shortestpathparser.cpp @@ -0,0 +1,153 @@ +// Copyright (C) 2017 Petros Koutsolampros + +// 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 3 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, see . + +#include "shortestpathparser.h" +#include "depthmapXcli/exceptions.h" +#include "depthmapXcli/parsingutils.h" +#include "depthmapXcli/runmethods.h" +#include "depthmapXcli/simpletimer.h" +#include "modules/vgapaths/core/vgaangularshortestpath.h" +#include "modules/vgapaths/core/vgametricshortestpath.h" +#include "modules/vgapaths/core/vgametricshortestpathtomany.h" +#include "modules/vgapaths/core/vgavisualshortestpath.h" +#include "salalib/entityparsing.h" +#include +#include + +using namespace depthmapX; + +void ShortestPathParser::parse(int argc, char **argv) { + + std::vector origins; + std::vector destinations; + for (int i = 1; i < argc; ++i) { + if (std::strcmp("-spo", argv[i]) == 0) { + ENFORCE_ARGUMENT("-spo", i) + if (!has_only_digits_dots_commas(argv[i])) { + std::stringstream message; + message << "Invalid origin point provided (" << argv[i] + << "). Should only contain digits dots and commas" << std::flush; + throw CommandLineException(message.str().c_str()); + } + origins.push_back(argv[i]); + } else if (std::strcmp("-spd", argv[i]) == 0) { + ENFORCE_ARGUMENT("-spd", i) + if (!has_only_digits_dots_commas(argv[i])) { + std::stringstream message; + message << "Invalid destination point provided (" << argv[i] + << "). Should only contain digits dots and commas" << std::flush; + throw CommandLineException(message.str().c_str()); + } + destinations.push_back(argv[i]); + } else if (std::strcmp("-spt", argv[i]) == 0) { + ENFORCE_ARGUMENT("-spt", i) + if (std::strcmp(argv[i], "angular") == 0) { + m_shortestPathType = ShortestPathType::ANGULAR; + } else if (std::strcmp(argv[i], "metric") == 0) { + m_shortestPathType = ShortestPathType::METRIC; + } else if (std::strcmp(argv[i], "visual") == 0) { + m_shortestPathType = ShortestPathType::VISUAL; + } else { + throw CommandLineException(std::string("Invalid Shortest Path type: ") + argv[i]); + } + } + } + + if (origins.empty()) { + throw CommandLineException("At least one origin point (-spo) must be provided"); + } else { + for (const auto &origin : origins) { + m_origins.push_back(EntityParsing::parsePoint(origin)); + } + } + if (destinations.empty()) { + throw CommandLineException("At least one destination point (-spd) must be provided"); + } else { + for (const auto &destination : destinations) { + m_destinations.push_back(EntityParsing::parsePoint(destination)); + } + } + if (m_shortestPathType == ShortestPathType::NONE) { + throw CommandLineException("Shortest path type (-spt) must be provided"); + } + if (m_shortestPathType != ShortestPathType::METRIC && m_destinations.size() > 1) { + throw CommandLineException("Multple shortest paths may only be calculated with metric depth currently"); + } +} + +void ShortestPathParser::run(const CommandLineParser &clp, IPerformanceSink &perfWriter) const { + auto mGraph = dm_runmethods::loadGraph(clp.getFileName().c_str(), perfWriter); + + std::cout << "ok\nSelecting cells... " << std::flush; + + auto &graphRegion = mGraph->getRegion(); + PointMap &map = mGraph->getDisplayedPointMap(); + + std::set pixelsFrom; + for (const Point2f &origin : m_origins) { + if (!graphRegion.contains(origin) || !map.getRegion().contains(origin)) { + throw depthmapX::RuntimeException("Origin point " + std::to_string(origin.x) + ", " + + std::to_string(origin.y) + " outside of target region"); + } + PixelRef pixelFrom = map.pixelate(origin); + if (!map.getPoint(pixelFrom).filled()) { + throw depthmapX::RuntimeException("Origin point " + std::to_string(origin.x) + ", " + + std::to_string(origin.y) + " not filled in target pointmap"); + } + pixelsFrom.insert(pixelFrom); + } + std::set pixelsTo; + for (const Point2f &destination : m_destinations) { + + if (!graphRegion.contains(destination) || !map.getRegion().contains(destination)) { + throw depthmapX::RuntimeException("Destination point " + std::to_string(destination.x) + ", " + + std::to_string(destination.y) + " outside of target region"); + } + PixelRef pixelTo = map.pixelate(destination); + if (!map.getPoint(pixelTo).filled()) { + throw depthmapX::RuntimeException("Destination point " + std::to_string(destination.x) + ", " + + std::to_string(destination.y) + " not filled in target pointmap"); + } + pixelsTo.insert(pixelTo); + } + + std::cout << "ok\nCalculating shortest path... " << std::flush; + + std::unique_ptr comm(new ICommunicator()); + + DO_TIMED( + "Calculating shortest path", switch (m_shortestPathType) { + case ShortestPathParser::ShortestPathType::ANGULAR: + VGAAngularShortestPath(map, *pixelsFrom.begin(), *pixelsTo.begin()).run(comm.get()); + break; + case ShortestPathParser::ShortestPathType::METRIC: + if (pixelsTo.size() == 1) { + VGAMetricShortestPath(map, pixelsFrom, *pixelsTo.begin()).run(comm.get()); + } else { + VGAMetricShortestPathToMany(map, pixelsFrom, pixelsTo).run(comm.get()); + } + break; + case ShortestPathParser::ShortestPathType::VISUAL: + VGAVisualShortestPath(map, *pixelsFrom.begin(), *pixelsTo.begin()).run(comm.get()); + break; + default: { + throw depthmapX::SetupCheckException("Error, unsupported shortest path mode"); + } + }); + + std::cout << " ok\nWriting out result..." << std::flush; + DO_TIMED("Writing graph", mGraph->write(clp.getOuputFile().c_str(), METAGRAPH_VERSION, false)) + std::cout << " ok" << std::endl; +} diff --git a/modules/vgapaths/cli/shortestpathparser.h b/modules/vgapaths/cli/shortestpathparser.h new file mode 100644 index 00000000..c5342beb --- /dev/null +++ b/modules/vgapaths/cli/shortestpathparser.h @@ -0,0 +1,49 @@ +// Copyright (C) 2017 Petros Koutsolampros + +// 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 3 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, see . + +#pragma once + +#include "depthmapXcli/imodeparser.h" +#include "genlib/p2dpoly.h" +#include + +class ShortestPathParser : public IModeParser { + public: + ShortestPathParser() : m_shortestPathType(ShortestPathType::NONE) {} + + virtual std::string getModeName() const { return "VGASHORTESTPATH"; } + + virtual std::string getHelp() const { + return "Mode options for pointmap VGASHORTESTPATH are:\n" + " -spo Origin point\n" + " -spd Destination point (can be given multiple times)\n" + " -spt Shortest Path type. One of metric, angular or visual\n"; + } + + enum class ShortestPathType { NONE, ANGULAR, METRIC, VISUAL }; + + virtual void parse(int argc, char **argv); + + virtual void run(const CommandLineParser &clp, IPerformanceSink &perfWriter) const; + + std::vector getOrigins() const { return m_origins; } + std::vector getDestinations() const { return m_destinations; } + ShortestPathType getShortestPathType() const { return m_shortestPathType; } + + private: + std::vector m_origins; + std::vector m_destinations; + ShortestPathType m_shortestPathType; +}; diff --git a/modules/vgapaths/cliTest/CMakeLists.txt b/modules/vgapaths/cliTest/CMakeLists.txt new file mode 100644 index 00000000..650169b3 --- /dev/null +++ b/modules/vgapaths/cliTest/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# 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 3 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, see . + +set(vgapathsclitest vgapathsclitest) +set(vgapathsclitest_SRCS + testshortestpathparser.cpp) + +set(modules_cliTest "${modules_cliTest}" "vgapathsclitest" CACHE INTERNAL "modules_cliTest" FORCE) + +add_compile_definitions(VGAPATHS_CLI_TEST_LIBRARY) + +add_library(${vgapathsclitest} OBJECT ${vgapathsclitest_SRCS}) diff --git a/modules/vgapaths/cliTest/testshortestpathparser.cpp b/modules/vgapaths/cliTest/testshortestpathparser.cpp new file mode 100644 index 00000000..4850ad03 --- /dev/null +++ b/modules/vgapaths/cliTest/testshortestpathparser.cpp @@ -0,0 +1,85 @@ +// Copyright (C) 2017 Petros Koutsolampros + +// 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 3 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, see . + +#include "cliTest/argumentholder.h" +#include "cliTest/selfcleaningfile.h" +#include "modules/vgapaths/cli/shortestpathparser.h" +#include + +TEST_CASE("ShortestPathParser Fail", "Error cases") { + SECTION("Missing argument to -spo") { + ShortestPathParser parser; + ArgumentHolder ah{"prog", "-spo"}; + REQUIRE_THROWS_WITH(parser.parse(ah.argc(), ah.argv()), Catch::Contains("-spo requires an argument")); + } + SECTION("Missing argument to -spd") { + ShortestPathParser parser; + ArgumentHolder ah{"prog", "-spd"}; + REQUIRE_THROWS_WITH(parser.parse(ah.argc(), ah.argv()), Catch::Contains("-spd requires an argument")); + } + SECTION("Missing argument to -spt") { + ShortestPathParser parser; + ArgumentHolder ah{"prog", "-spt"}; + REQUIRE_THROWS_WITH(parser.parse(ah.argc(), ah.argv()), Catch::Contains("-spt requires an argument")); + } + + SECTION("rubbish input to -spo") { + ShortestPathParser parser; + ArgumentHolder ah{"prog", "-spo", "foo"}; + REQUIRE_THROWS_WITH( + parser.parse(ah.argc(), ah.argv()), + Catch::Contains("Invalid origin point provided (foo). Should only contain digits dots and commas")); + } + + SECTION("rubbish input to -spd") { + ShortestPathParser parser; + ArgumentHolder ah{"prog", "-spd", "foo"}; + REQUIRE_THROWS_WITH( + parser.parse(ah.argc(), ah.argv()), + Catch::Contains("Invalid destination point provided (foo). Should only contain digits dots and commas")); + } + + SECTION("Invalid Shortest Path type") { + ShortestPathParser parser; + ArgumentHolder ah{"prog", "-spt", "foo"}; + REQUIRE_THROWS_WITH(parser.parse(ah.argc(), ah.argv()), "Invalid Shortest Path type: foo"); + } +} + +TEST_CASE("ShortestPathParser Success", "Read successfully") { + ShortestPathParser parser; + double x1 = 1.0; + double y1 = 2.0; + double x2 = 1.1; + double y2 = 1.2; + std::string type = "metric"; + + std::stringstream p1; + p1 << x1 << "," << y1 << std::flush; + std::stringstream p2; + p2 << x2 << "," << y2 << std::flush; + + ArgumentHolder ah{"prog", "-spo", p1.str(), "-spd", p2.str(), "-spt", type}; + parser.parse(ah.argc(), ah.argv()); + + Point2f origin = *parser.getOrigins().begin(); + Point2f destination = *parser.getDestinations().begin(); + ShortestPathParser::ShortestPathType shortestPathType = parser.getShortestPathType(); + REQUIRE(origin.x == Approx(x1)); + REQUIRE(origin.y == Approx(y1)); + REQUIRE(destination.x == Approx(x2)); + REQUIRE(destination.y == Approx(y2)); + REQUIRE(shortestPathType == ShortestPathParser::ShortestPathType::METRIC); +} diff --git a/modules/vgapaths/core/CMakeLists.txt b/modules/vgapaths/core/CMakeLists.txt new file mode 100644 index 00000000..f942f811 --- /dev/null +++ b/modules/vgapaths/core/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# 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 3 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, see . + +set(vgapathscore vgapathscore) +set(vgapathscore_SRCS + vgavisualshortestpath.h + vgavisualshortestpath.cpp + vgametricshortestpathtomany.h + vgametricshortestpathtomany.cpp + vgametricshortestpath.h + vgametricshortestpath.cpp + vgametricdepthlinkcost.h + vgametricdepthlinkcost.cpp + vgaisovistzone.h + vgaisovistzone.cpp + vgaangularshortestpath.h + vgaangularshortestpath.cpp + extractlinkdata.h + extractlinkdata.cpp) + +set(modules_core "${modules_core}" "vgapathscore" CACHE INTERNAL "modules_core" FORCE) + +add_compile_definitions(VGAPATHS_CORE_LIBRARY) + +add_library(${vgapathscore} OBJECT ${vgapathscore_SRCS}) diff --git a/modules/vgapaths/core/extractlinkdata.cpp b/modules/vgapaths/core/extractlinkdata.cpp new file mode 100644 index 00000000..40d61e67 --- /dev/null +++ b/modules/vgapaths/core/extractlinkdata.cpp @@ -0,0 +1,45 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// 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 3 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, see . + +#include "extractlinkdata.h" + +#include "genlib/stringutils.h" + +bool ExtractLinkData::run(Communicator *) { + + auto &attributes = m_map.getAttributeTable(); + + int angular_cost_col = attributes.insertOrResetColumn("Link Angular Cost"); + int metric_cost_col = attributes.insertOrResetColumn("Link Metric Cost"); + int link_to_col = attributes.insertOrResetColumn("Link To"); + int visual_cost_col = attributes.insertOrResetColumn("Link Visual Cost"); + + for (auto &row : attributes) { + PixelRef pix = PixelRef(row.getKey().value); + Point &p = m_map.getPoint(pix); + PixelRef mergePixel = p.getMergePixel(); + if (!mergePixel.empty()) { + row.getRow().setValue(link_to_col, static_cast(mergePixel)); + row.getRow().setValue(visual_cost_col, 1); + row.getRow().setValue(metric_cost_col, dist(pix, mergePixel) * m_map.getSpacing()); + row.getRow().setValue(angular_cost_col, 1); + } + } + + return true; +} diff --git a/modules/vgapaths/core/extractlinkdata.h b/modules/vgapaths/core/extractlinkdata.h new file mode 100644 index 00000000..d98913bd --- /dev/null +++ b/modules/vgapaths/core/extractlinkdata.h @@ -0,0 +1,34 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// 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 3 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, see . + +#pragma once + +#include "salalib/ianalysis.h" +#include "salalib/options.h" +#include "salalib/pixelref.h" +#include "salalib/pointdata.h" + +class ExtractLinkData : public IAnalysis { + private: + PointMap &m_map; + + public: + std::string getAnalysisName() const override { return "Extract Link Data"; } + ExtractLinkData(PointMap &map) : m_map(map) {} + bool run(Communicator *comm) override; +}; diff --git a/modules/vgapaths/core/vgaangularshortestpath.cpp b/modules/vgapaths/core/vgaangularshortestpath.cpp new file mode 100644 index 00000000..e7d8d59e --- /dev/null +++ b/modules/vgapaths/core/vgaangularshortestpath.cpp @@ -0,0 +1,155 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// 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 3 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, see . + +#include "vgaangularshortestpath.h" + +#include "genlib/stringutils.h" + +bool VGAAngularShortestPath::run(Communicator *) { + + auto &attributes = m_map.getAttributeTable(); + + int path_col = attributes.insertOrResetColumn("Angular Shortest Path"); + int linked_col = attributes.insertOrResetColumn("Angular Shortest Path Linked"); + int order_col = attributes.insertOrResetColumn("Angular Shortest Path Order"); + int zone_col = attributes.insertOrResetColumn("Angular Shortest Path Zone"); + + for (auto &row : attributes) { + PixelRef pix = PixelRef(row.getKey().value); + Point &p = m_map.getPoint(pix); + p.m_misc = 0; + p.m_dist = 0.0f; + p.m_cumangle = -1.0f; + } + + // in order to calculate Penn angle, the MetricPair becomes a metric triple... + std::set search_list; // contains root point + + search_list.insert(AngularTriple(0.0f, m_pixelFrom, NoPixel)); + m_map.getPoint(m_pixelFrom).m_cumangle = 0.0f; + + // note that m_misc is used in a different manner to analyseGraph / PointDepth + // here it marks the node as used in calculation only + std::map parents; + bool pixelFound = false; + while (search_list.size()) { + std::set::iterator it = search_list.begin(); + AngularTriple here = *it; + search_list.erase(it); + Point &p = m_map.getPoint(here.pixel); + std::set newPixels; + std::set mergePixels; + // nb, the filled check is necessary as diagonals seem to be stored with 'gaps' left in + if (p.filled() && p.m_misc != ~0) { + p.getNode().extractAngular(newPixels, &m_map, here); + p.m_misc = ~0; + if (!p.getMergePixel().empty()) { + Point &p2 = m_map.getPoint(p.getMergePixel()); + if (p2.m_misc != ~0) { + auto newTripleIter = newPixels.insert(AngularTriple(here.angle, p.getMergePixel(), NoPixel)); + p2.m_cumangle = p.m_cumangle; + p2.getNode().extractAngular(mergePixels, &m_map, *newTripleIter.first); + for (auto &pixel : mergePixels) { + parents[pixel.pixel] = p.getMergePixel(); + } + p2.m_misc = ~0; + } + } + } + for (auto &pixel : newPixels) { + parents[pixel.pixel] = here.pixel; + } + newPixels.insert(mergePixels.begin(), mergePixels.end()); + for (auto &pixel : newPixels) { + if (pixel.pixel == m_pixelTo) { + pixelFound = true; + } + } + if (!pixelFound) + search_list.insert(newPixels.begin(), newPixels.end()); + } + + int linePixelCounter = 0; + auto pixelToParent = parents.find(m_pixelTo); + if (pixelToParent != parents.end()) { + + for (auto &row : attributes) { + PixelRef pix = PixelRef(row.getKey().value); + Point &p = m_map.getPoint(pix); + p.m_misc = 0; + p.m_dist = 0.0f; + p.m_cumangle = -1.0f; + } + + int counter = 0; + + AttributeRow &lastPixelRow = attributes.getRow(AttributeKey(m_pixelTo)); + lastPixelRow.setValue(order_col, counter); + counter++; + auto currParent = pixelToParent; + counter++; + while (currParent != parents.end()) { + Point &p = m_map.getPoint(currParent->second); + AttributeRow &row = attributes.getRow(AttributeKey(currParent->second)); + row.setValue(order_col, counter); + + if (!p.getMergePixel().empty() && p.getMergePixel() == currParent->first) { + row.setValue(linked_col, 1); + lastPixelRow.setValue(linked_col, 1); + } else { + // apparently we can't just have 1 number in the whole column + row.setValue(linked_col, 0); + auto pixelated = m_map.quickPixelateLine(currParent->first, currParent->second); + for (auto &linePixel : pixelated) { + auto *linePixelRow = attributes.getRowPtr(AttributeKey(linePixel)); + if (linePixelRow != 0) { + linePixelRow->setValue(path_col, linePixelCounter++); + linePixelRow->setValue(zone_col, 1); + + std::set newPixels; + Point &p = m_map.getPoint(linePixel); + p.getNode().extractAngular(newPixels, &m_map, AngularTriple(0.0f, linePixel, NoPixel)); + for (auto &zonePixel : newPixels) { + auto *zonePixelRow = attributes.getRowPtr(AttributeKey(zonePixel.pixel)); + if (zonePixelRow != 0) { + double zoneLineDist = dist(linePixel, zonePixel.pixel); + float currZonePixelVal = zonePixelRow->getValue(zone_col); + if (currZonePixelVal == -1 || 1.0f / (zoneLineDist + 1) > currZonePixelVal) { + zonePixelRow->setValue(zone_col, 1.0f / (zoneLineDist + 1)); + } + m_map.getPoint(zonePixel.pixel).m_misc = 0; + m_map.getPoint(zonePixel.pixel).m_extent = zonePixel.pixel; + } + } + } + } + } + + lastPixelRow = row; + currParent = parents.find(currParent->second); + counter++; + } + + m_map.overrideDisplayedAttribute(-2); + m_map.setDisplayedAttribute(order_col); + + return true; + } + + return false; +} diff --git a/modules/vgapaths/core/vgaangularshortestpath.h b/modules/vgapaths/core/vgaangularshortestpath.h new file mode 100644 index 00000000..044232fa --- /dev/null +++ b/modules/vgapaths/core/vgaangularshortestpath.h @@ -0,0 +1,36 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// 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 3 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, see . + +#pragma once + +#include "salalib/ianalysis.h" +#include "salalib/options.h" +#include "salalib/pixelref.h" +#include "salalib/pointdata.h" + +class VGAAngularShortestPath : public IAnalysis { + private: + PointMap &m_map; + PixelRef m_pixelFrom, m_pixelTo; + + public: + std::string getAnalysisName() const override { return "Angular Shortest Path"; } + bool run(Communicator *comm) override; + VGAAngularShortestPath(PointMap &map, PixelRef pixelFrom, PixelRef pixelTo) + : m_map(map), m_pixelFrom(pixelFrom), m_pixelTo(pixelTo) {} +}; diff --git a/modules/vgapaths/core/vgaisovistzone.cpp b/modules/vgapaths/core/vgaisovistzone.cpp new file mode 100644 index 00000000..efc5b1e1 --- /dev/null +++ b/modules/vgapaths/core/vgaisovistzone.cpp @@ -0,0 +1,123 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2019, Petros Koutsolampros + +// 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 3 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, see . + +#include "vgaisovistzone.h" + +#include "salalib/salaprogram.h" + +#include "genlib/stringutils.h" + +#include + +bool VGAIsovistZone::run(Communicator *) { + + auto &attributes = m_map.getAttributeTable(); + + if (m_originPointSets.empty()) { + return false; + } + int zoneColumnIndex = -1; + for (auto originPointSet : m_originPointSets) { + std::string zoneColumnName = "Isovist Zone Distance"; + std::string inverseZoneColumnName = "Isovist Zone Inverse Square Distance"; + + std::string originPointSetName = originPointSet.first; + auto originPoints = originPointSet.second; + + if (m_originPointSets.size() > 1) { + zoneColumnName += " [" + originPointSetName + "]"; + inverseZoneColumnName += " [" + originPointSetName + "]"; + } + + if (m_restrictDistance > 0) { + + std::stringstream restrictionText; + restrictionText << std::fixed << std::setprecision(2) << " (" << m_restrictDistance << ")" << std::flush; + + zoneColumnName += restrictionText.str(); + inverseZoneColumnName += restrictionText.str(); + } + zoneColumnIndex = attributes.insertOrResetColumn(zoneColumnName); + + for (PixelRef ref : originPoints) { + AttributeRow &row = attributes.getRow(AttributeKey(ref)); + row.setValue(zoneColumnIndex, 0); + Point &lp = m_map.getPoint(ref); + std::set newPixels; + extractMetric(lp.getNode(), newPixels, m_map, MetricTriple(0.0f, ref, NoPixel)); + for (auto &zonePixel : newPixels) { + auto *zonePixelRow = attributes.getRowPtr(AttributeKey(zonePixel.pixel)); + if (zonePixelRow != 0) { + double zoneLineDist = dist(ref, zonePixel.pixel) * m_map.getSpacing(); + float currZonePixelVal = zonePixelRow->getValue(zoneColumnIndex); + if ((currZonePixelVal == -1 || zoneLineDist < currZonePixelVal) && + (m_restrictDistance <= 0 || (m_restrictDistance > 0 && zoneLineDist < m_restrictDistance))) { + zonePixelRow->setValue(zoneColumnIndex, zoneLineDist); + } + } + } + } + int inverseZoneColumnIndex = attributes.insertOrResetColumn(inverseZoneColumnName); + setColumnFormulaAndUpdate(m_map, inverseZoneColumnIndex, "1/((value(\"" + zoneColumnName + "\") + 1) ^ 2)", + false); + } + m_map.overrideDisplayedAttribute(-2); + m_map.setDisplayedAttribute(zoneColumnIndex); + + return true; +} + +void VGAIsovistZone::extractMetric(Node n, std::set &pixels, PointMap &map, const MetricTriple &curs) { + for (int i = 0; i < 32; i++) { + Bin &bin = n.bin(i); + for (auto pixVec : bin.m_pixel_vecs) { + for (PixelRef pix = pixVec.start(); pix.col(bin.m_dir) <= pixVec.end().col(bin.m_dir);) { + Point &pt = map.getPoint(pix); + if (pt.filled()) { + pixels.insert(MetricTriple(0, pix, curs.pixel)); + } + pix.move(bin.m_dir); + } + } + } +} + +void VGAIsovistZone::setColumnFormulaAndUpdate(PointMap &pointmap, int columnIndex, std::string formula, + bool selectionOnly) { + SalaObj program_context; + SalaGrf graph; + graph.map.point = &pointmap; + program_context = SalaObj(SalaObj::S_POINTMAPOBJ, graph); + + std::istringstream stream(formula); + SalaProgram proggy(program_context); + if (!proggy.parse(stream)) { + throw depthmapX::RuntimeException("There was an error parsing your formula:\n\n" + + proggy.getLastErrorMessage()); + } else { + bool programCompleted; + if (selectionOnly) { + programCompleted = proggy.runupdate(columnIndex, pointmap.getSelSet()); + } else { + programCompleted = proggy.runupdate(columnIndex); + } + if (!programCompleted) { + throw depthmapX::RuntimeException("There was an error parsing your formula:\n\n" + + proggy.getLastErrorMessage()); + } + } + program_context.getTable()->getColumn(columnIndex).setFormula(formula); +} diff --git a/modules/vgapaths/core/vgaisovistzone.h b/modules/vgapaths/core/vgaisovistzone.h new file mode 100644 index 00000000..4e6fdde2 --- /dev/null +++ b/modules/vgapaths/core/vgaisovistzone.h @@ -0,0 +1,44 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2019, Petros Koutsolampros + +// 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 3 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, see . + +#pragma once + +#include "salalib/ianalysis.h" +#include "salalib/pixelref.h" +#include "salalib/pointdata.h" + +class VGAIsovistZone : public IAnalysis { + private: + PointMap &m_map; + std::map> m_originPointSets; + float m_restrictDistance; + + struct MetricPoint { + Point *m_point = nullptr; + }; + MetricPoint &getMetricPoint(depthmapX::ColumnMatrix &metricPoints, PixelRef ref) { + return (metricPoints(static_cast(ref.y), static_cast(ref.x))); + } + void extractMetric(Node n, std::set &pixels, PointMap &map, const MetricTriple &curs); + void setColumnFormulaAndUpdate(PointMap &pointmap, int columnIndex, std::string formula, bool selectionOnly); + + public: + std::string getAnalysisName() const override { return "Path Zone"; } + bool run(Communicator *) override; + VGAIsovistZone(PointMap &map, std::map> originPointSets, + float restrictDistance = -1) + : m_map(map), m_originPointSets(originPointSets), m_restrictDistance(restrictDistance) {} +}; diff --git a/modules/vgapaths/core/vgametricdepthlinkcost.cpp b/modules/vgapaths/core/vgametricdepthlinkcost.cpp new file mode 100644 index 00000000..f8647eed --- /dev/null +++ b/modules/vgapaths/core/vgametricdepthlinkcost.cpp @@ -0,0 +1,108 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// 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 3 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, see . + +#include "vgametricdepthlinkcost.h" + +#include "genlib/stringutils.h" + +bool VGAMetricDepthLinkCost::run(Communicator *) { + + AttributeTable &attributes = m_map.getAttributeTable(); + + // custom linking costs from the attribute table + int link_metric_cost_col = attributes.getOrInsertColumn("Link Metric Cost"); + + int path_length_col = attributes.insertOrResetColumn("Metric Step Depth"); + + depthmapX::ColumnMatrix metricPoints(m_map.getRows(), m_map.getCols()); + + for (auto &row : attributes) { + PixelRef pix = PixelRef(row.getKey().value); + MetricPoint &pnt = getMetricPoint(metricPoints, pix); + pnt.m_point = &(m_map.getPoint(pix)); + if (link_metric_cost_col != -1) { + float linkCost = row.getRow().getValue(link_metric_cost_col); + if (linkCost > 0) + pnt.m_linkCost += linkCost; + } + } + + // in order to calculate Penn angle, the MetricPair becomes a metric triple... + std::set search_list; // contains root point + + for (auto &sel : m_pixelsFrom) { + search_list.insert(MetricTriple(0.0f, sel, NoPixel)); + } + + while (search_list.size()) { + std::set::iterator it = search_list.begin(); + MetricTriple here = *it; + search_list.erase(it); + MetricPoint &mp = getMetricPoint(metricPoints, here.pixel); + // nb, the filled check is necessary as diagonals seem to be stored with 'gaps' left in + if (mp.m_point->filled() && (mp.m_unseen || (here.dist < mp.m_dist))) { + extractMetric(mp.m_point->getNode(), metricPoints, search_list, &m_map, here); + mp.m_dist = here.dist; + mp.m_unseen = false; + if (!mp.m_point->getMergePixel().empty()) { + MetricPoint &mp2 = getMetricPoint(metricPoints, mp.m_point->getMergePixel()); + if (mp2.m_unseen || (here.dist + mp2.m_linkCost < mp2.m_dist)) { + mp2.m_dist = here.dist + mp2.m_linkCost; + mp2.m_unseen = false; + extractMetric(mp2.m_point->getNode(), metricPoints, search_list, &m_map, + MetricTriple(mp2.m_dist, mp.m_point->getMergePixel(), NoPixel)); + } + } + } + } + + for (auto &row : attributes) { + PixelRef pix = PixelRef(row.getKey().value); + MetricPoint &pnt = getMetricPoint(metricPoints, pix); + row.getRow().setValue(path_length_col, float(pnt.m_dist)); + } + m_map.setDisplayedAttribute(-2); + m_map.setDisplayedAttribute(path_length_col); + + return true; +} + +void VGAMetricDepthLinkCost::extractMetric(Node n, depthmapX::ColumnMatrix &metricPoints, + std::set &pixels, PointMap *pointdata, + const MetricTriple &curs) { + MetricPoint &cursMP = getMetricPoint(metricPoints, curs.pixel); + if (curs.dist == 0.0f || cursMP.m_point->blocked() || pointdata->blockedAdjacent(curs.pixel)) { + for (int i = 0; i < 32; i++) { + Bin &bin = n.bin(i); + for (auto pixVec : bin.m_pixel_vecs) { + for (PixelRef pix = pixVec.start(); pix.col(bin.m_dir) <= pixVec.end().col(bin.m_dir);) { + MetricPoint &mpt = getMetricPoint(metricPoints, pix); + // the nullptr check is unfortunately required because somehow depthmap stores + // neighbour pixels that are not really filled.. + if (mpt.m_point != nullptr && mpt.m_unseen && + (mpt.m_dist == -1 || + (curs.dist + pointdata->getSpacing() * dist(pix, curs.pixel) < mpt.m_dist))) { + mpt.m_dist = curs.dist + pointdata->getSpacing() * (float)dist(pix, curs.pixel); + pixels.insert(MetricTriple(mpt.m_dist, pix, curs.pixel)); + } + pix.move(bin.m_dir); + } + } + } + } +} diff --git a/modules/vgapaths/core/vgametricdepthlinkcost.h b/modules/vgapaths/core/vgametricdepthlinkcost.h new file mode 100644 index 00000000..4b29e81c --- /dev/null +++ b/modules/vgapaths/core/vgametricdepthlinkcost.h @@ -0,0 +1,47 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// 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 3 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, see . + +#pragma once + +#include "salalib/ianalysis.h" +#include "salalib/options.h" +#include "salalib/pixelref.h" +#include "salalib/pointdata.h" + +class VGAMetricDepthLinkCost : public IAnalysis { + private: + PointMap &m_map; + std::set m_pixelsFrom; + + struct MetricPoint { + Point *m_point = nullptr; + float m_linkCost = 0; + float m_dist = -1.0f; + bool m_unseen = true; + }; + MetricPoint &getMetricPoint(depthmapX::ColumnMatrix &metricPoints, PixelRef ref) { + return (metricPoints(static_cast(ref.y), static_cast(ref.x))); + } + void extractMetric(Node n, depthmapX::ColumnMatrix &metricPoints, std::set &pixels, + PointMap *pointdata, const MetricTriple &curs); + + public: + std::string getAnalysisName() const override { return "Metric Depth"; } + bool run(Communicator *) override; + VGAMetricDepthLinkCost(PointMap &map, std::set pixelsFrom) : m_map(map), m_pixelsFrom(pixelsFrom) {} +}; diff --git a/modules/vgapaths/core/vgametricshortestpath.cpp b/modules/vgapaths/core/vgametricshortestpath.cpp new file mode 100644 index 00000000..7f485b3e --- /dev/null +++ b/modules/vgapaths/core/vgametricshortestpath.cpp @@ -0,0 +1,178 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// 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 3 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, see . + +#include "vgametricshortestpath.h" + +#include "genlib/stringutils.h" + +bool VGAMetricShortestPath::run(Communicator *) { + + auto &attributes = m_map.getAttributeTable(); + + // custom linking costs from the attribute table + int link_metric_cost_col = attributes.getOrInsertColumn("Link Metric Cost"); + + int path_col = attributes.insertOrResetColumn("Metric Shortest Path"); + int dist_col = attributes.insertOrResetColumn("Metric Shortest Path Distance"); + int linked_col = attributes.insertOrResetColumn("Metric Shortest Path Linked"); + int order_col = attributes.insertOrResetColumn("Metric Shortest Path Order"); + + depthmapX::ColumnMatrix metricPoints(m_map.getRows(), m_map.getCols()); + + for (auto &row : attributes) { + PixelRef pix = PixelRef(row.getKey().value); + MetricPoint &pnt = getMetricPoint(metricPoints, pix); + pnt.m_point = &(m_map.getPoint(pix)); + if (link_metric_cost_col != -1) { + float linkCost = row.getRow().getValue(link_metric_cost_col); + if (linkCost > 0) + pnt.m_linkCost += linkCost; + } + } + + // in order to calculate Penn angle, the MetricPair becomes a metric triple... + std::set search_list; // contains root point + + for (const PixelRef &pixelFrom : m_pixelsFrom) { + search_list.insert(MetricTriple(0.0f, pixelFrom, NoPixel)); + } + + // note that m_misc is used in a different manner to analyseGraph / PointDepth + // here it marks the node as used in calculation only + std::map parents; + bool pixelFound = false; + while (search_list.size()) { + std::set::iterator it = search_list.begin(); + MetricTriple here = *it; + search_list.erase(it); + MetricPoint &mp = getMetricPoint(metricPoints, here.pixel); + std::set newPixels; + std::set mergePixels; + if (mp.m_unseen || (here.dist < mp.m_dist)) { + extractMetric(mp.m_point->getNode(), metricPoints, newPixels, &m_map, here); + mp.m_dist = here.dist; + mp.m_unseen = false; + if (!mp.m_point->getMergePixel().empty()) { + MetricPoint &mp2 = getMetricPoint(metricPoints, mp.m_point->getMergePixel()); + if (mp2.m_unseen || (here.dist + mp2.m_linkCost < mp2.m_dist)) { + mp2.m_dist = here.dist + mp2.m_linkCost; + mp2.m_unseen = false; + + auto newTripleIter = + newPixels.insert(MetricTriple(mp2.m_dist, mp.m_point->getMergePixel(), NoPixel)); + extractMetric(mp2.m_point->getNode(), metricPoints, mergePixels, &m_map, *newTripleIter.first); + for (auto &pixel : mergePixels) { + parents[pixel.pixel] = mp.m_point->getMergePixel(); + } + } + } + for (auto &pixel : newPixels) { + parents[pixel.pixel] = here.pixel; + } + newPixels.insert(mergePixels.begin(), mergePixels.end()); + for (auto &pixel : newPixels) { + if (pixel.pixel == m_pixelTo) { + pixelFound = true; + } + } + if (!pixelFound) + search_list.insert(newPixels.begin(), newPixels.end()); + } + } + + int linePixelCounter = 0; + auto pixelToParent = parents.find(m_pixelTo); + if (pixelToParent != parents.end()) { + + for (auto &row : attributes) { + PixelRef pix = PixelRef(row.getKey().value); + MetricPoint &mp = getMetricPoint(metricPoints, pix); + mp.m_cumdist = mp.m_dist; + mp.m_dist = -1.0f; + mp.m_unseen = true; + } + + int counter = 0; + for (const PixelRef &pixelFrom : m_pixelsFrom) { + getMetricPoint(metricPoints, pixelFrom).m_cumdist = 0; + } + AttributeRow &lastPixelRow = attributes.getRow(AttributeKey(m_pixelTo)); + MetricPoint &mp = getMetricPoint(metricPoints, m_pixelTo); + lastPixelRow.setValue(order_col, counter); + lastPixelRow.setValue(dist_col, mp.m_cumdist); + counter++; + auto currParent = pixelToParent; + while (currParent != parents.end()) { + MetricPoint &mp = getMetricPoint(metricPoints, currParent->second); + AttributeRow &row = attributes.getRow(AttributeKey(currParent->second)); + row.setValue(order_col, counter); + row.setValue(dist_col, mp.m_cumdist); + + if (!mp.m_point->getMergePixel().empty() && mp.m_point->getMergePixel() == currParent->first) { + row.setValue(linked_col, 1); + lastPixelRow.setValue(linked_col, 1); + } else { + // apparently we can't just have 1 number in the whole column + row.setValue(linked_col, 0); + auto pixelated = m_map.quickPixelateLine(currParent->first, currParent->second); + for (auto &linePixel : pixelated) { + auto *linePixelRow = attributes.getRowPtr(AttributeKey(linePixel)); + if (linePixelRow != 0) { + linePixelRow->setValue(path_col, linePixelCounter++); + } + } + } + + lastPixelRow = row; + currParent = parents.find(currParent->second); + counter++; + } + + m_map.overrideDisplayedAttribute(-2); + m_map.setDisplayedAttribute(order_col); + + return true; + } + + return false; +} + +void VGAMetricShortestPath::extractMetric(Node n, depthmapX::ColumnMatrix &metricPoints, + std::set &pixels, PointMap *pointdata, + const MetricTriple &curs) { + MetricPoint &cursMP = getMetricPoint(metricPoints, curs.pixel); + if (curs.dist == 0.0f || cursMP.m_point->blocked() || pointdata->blockedAdjacent(curs.pixel)) { + for (int i = 0; i < 32; i++) { + Bin &bin = n.bin(i); + for (auto pixVec : bin.m_pixel_vecs) { + for (PixelRef pix = pixVec.start(); pix.col(bin.m_dir) <= pixVec.end().col(bin.m_dir);) { + MetricPoint &mpt = getMetricPoint(metricPoints, pix); + // the nullptr check is unfortunately required because somehow depthmap stores + // neighbour pixels that are not really filled.. + if ((mpt.m_point != nullptr && mpt.m_unseen && + (mpt.m_dist == -1 || + (curs.dist + pointdata->getSpacing() * dist(pix, curs.pixel) < mpt.m_dist)))) { + mpt.m_dist = curs.dist + pointdata->getSpacing() * (float)dist(pix, curs.pixel); + pixels.insert(MetricTriple(mpt.m_dist, pix, curs.pixel)); + } + pix.move(bin.m_dir); + } + } + } + } +} diff --git a/modules/vgapaths/core/vgametricshortestpath.h b/modules/vgapaths/core/vgametricshortestpath.h new file mode 100644 index 00000000..47ee7580 --- /dev/null +++ b/modules/vgapaths/core/vgametricshortestpath.h @@ -0,0 +1,50 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// 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 3 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, see . + +#pragma once + +#include "salalib/ianalysis.h" +#include "salalib/options.h" +#include "salalib/pixelref.h" +#include "salalib/pointdata.h" + +class VGAMetricShortestPath : public IAnalysis { + private: + PointMap &m_map; + std::set m_pixelsFrom; + PixelRef m_pixelTo; + + struct MetricPoint { + Point *m_point = nullptr; + float m_linkCost = 0; + float m_dist = -1.0f; + float m_cumdist = -1.0f; + bool m_unseen = true; + }; + MetricPoint &getMetricPoint(depthmapX::ColumnMatrix &metricPoints, PixelRef ref) { + return (metricPoints(static_cast(ref.y), static_cast(ref.x))); + } + void extractMetric(Node n, depthmapX::ColumnMatrix &metricPoints, std::set &pixels, + PointMap *pointdata, const MetricTriple &curs); + + public: + std::string getAnalysisName() const override { return "Metric Shortest Path"; } + bool run(Communicator *) override; + VGAMetricShortestPath(PointMap &map, std::set pixelsFrom, PixelRef pixelTo) + : m_map(map), m_pixelsFrom(pixelsFrom), m_pixelTo(pixelTo) {} +}; diff --git a/modules/vgapaths/core/vgametricshortestpathtomany.cpp b/modules/vgapaths/core/vgametricshortestpathtomany.cpp new file mode 100644 index 00000000..b0e2b9c7 --- /dev/null +++ b/modules/vgapaths/core/vgametricshortestpathtomany.cpp @@ -0,0 +1,190 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// 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 3 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, see . + +#include "vgametricshortestpathtomany.h" + +#include "genlib/stringutils.h" + +bool VGAMetricShortestPathToMany::run(Communicator *) { + + auto &attributes = m_map.getAttributeTable(); + + // custom linking costs from the attribute table + int link_metric_cost_col = attributes.getOrInsertColumn("Link Metric Cost"); + + depthmapX::ColumnMatrix metricPoints(m_map.getRows(), m_map.getCols()); + + for (auto &row : attributes) { + PixelRef pix = PixelRef(row.getKey().value); + MetricPoint &pnt = getMetricPoint(metricPoints, pix); + pnt.m_point = &(m_map.getPoint(pix)); + if (link_metric_cost_col != -1) { + float linkCost = row.getRow().getValue(link_metric_cost_col); + if (linkCost > 0) + pnt.m_linkCost += linkCost; + } + } + + // in order to calculate Penn angle, the MetricPair becomes a metric triple... + std::set search_list; // contains root point + + for (const PixelRef &pixelFrom : m_pixelsFrom) { + search_list.insert(MetricTriple(0.0f, pixelFrom, NoPixel)); + } + + // note that m_misc is used in a different manner to analyseGraph / PointDepth + // here it marks the node as used in calculation only + std::map parents; + std::set pixelsTo = m_pixelsTo; // consumable set + while (search_list.size()) { + std::set::iterator it = search_list.begin(); + MetricTriple here = *it; + search_list.erase(it); + MetricPoint &mp = getMetricPoint(metricPoints, here.pixel); + std::set newPixels; + std::set mergePixels; + if (mp.m_unseen || (here.dist < mp.m_dist)) { + extractMetric(mp.m_point->getNode(), metricPoints, newPixels, &m_map, here); + mp.m_dist = here.dist; + mp.m_unseen = false; + if (!mp.m_point->getMergePixel().empty()) { + MetricPoint &mp2 = getMetricPoint(metricPoints, mp.m_point->getMergePixel()); + if (mp2.m_unseen || (here.dist + mp2.m_linkCost < mp2.m_dist)) { + mp2.m_dist = here.dist + mp2.m_linkCost; + mp2.m_unseen = false; + + auto newTripleIter = + newPixels.insert(MetricTriple(mp2.m_dist, mp.m_point->getMergePixel(), NoPixel)); + extractMetric(mp2.m_point->getNode(), metricPoints, mergePixels, &m_map, *newTripleIter.first); + for (auto &pixel : mergePixels) { + parents[pixel.pixel] = mp.m_point->getMergePixel(); + } + } + } + for (auto &pixel : newPixels) { + parents[pixel.pixel] = here.pixel; + } + newPixels.insert(mergePixels.begin(), mergePixels.end()); + for (auto &pixel : newPixels) { + auto it = pixelsTo.find(pixel.pixel); + if (it != pixelsTo.end()) { + pixelsTo.erase(it); + } + } + if (pixelsTo.size() != 0) + search_list.insert(newPixels.begin(), newPixels.end()); + } + } + + for (const PixelRef &pixelFrom : m_pixelsFrom) { + getMetricPoint(metricPoints, pixelFrom).m_dist = 0; + } + + std::map> columns; + for (PixelRef ref : m_pixelsTo) { + columns[ref].push_back(attributes.insertOrResetColumn("Metric Shortest Path " + std::to_string(ref))); + } + for (PixelRef ref : m_pixelsTo) { + columns[ref].push_back(attributes.insertOrResetColumn("Metric Shortest Path Distance " + std::to_string(ref))); + } + for (PixelRef ref : m_pixelsTo) { + columns[ref].push_back(attributes.insertOrResetColumn("Metric Shortest Path Linked " + std::to_string(ref))); + } + for (PixelRef ref : m_pixelsTo) { + columns[ref].push_back(attributes.insertOrResetColumn("Metric Shortest Path Order " + std::to_string(ref))); + } + + for (PixelRef pixelTo : m_pixelsTo) { + const std::vector &pixelToCols = columns[pixelTo]; + int path_col = pixelToCols[0]; + int dist_col = pixelToCols[1]; + int linked_col = pixelToCols[2]; + int order_col = pixelToCols[3]; + auto pixelToParent = parents.find(pixelTo); + if (pixelToParent != parents.end()) { + + int counter = 0; + int linePixelCounter = 0; + AttributeRow &lastPixelRow = attributes.getRow(AttributeKey(pixelTo)); + MetricPoint &mp = getMetricPoint(metricPoints, pixelTo); + lastPixelRow.setValue(order_col, counter); + lastPixelRow.setValue(dist_col, mp.m_dist); + counter++; + auto currParent = pixelToParent; + while (currParent != parents.end()) { + MetricPoint &mp = getMetricPoint(metricPoints, currParent->second); + AttributeRow &row = attributes.getRow(AttributeKey(currParent->second)); + row.setValue(order_col, counter); + row.setValue(dist_col, mp.m_dist); + + if (!mp.m_point->getMergePixel().empty() && mp.m_point->getMergePixel() == currParent->first) { + row.setValue(linked_col, 1); + lastPixelRow.setValue(linked_col, 1); + } else { + // apparently we can't just have 1 number in the whole column + row.setValue(linked_col, 0); + + auto pixelated = m_map.quickPixelateLine(currParent->first, currParent->second); + for (auto &linePixel : pixelated) { + auto *linePixelRow = attributes.getRowPtr(AttributeKey(linePixel)); + if (linePixelRow != 0) { + linePixelRow->setValue(path_col, linePixelCounter++); + } + } + } + + lastPixelRow = row; + currParent = parents.find(currParent->second); + counter++; + } + } + } + if (m_pixelsTo.size() > 0) { + m_map.overrideDisplayedAttribute(-2); + m_map.setDisplayedAttribute(columns[*m_pixelsTo.begin()][0]); + + return true; + } + + return false; +} + +void VGAMetricShortestPathToMany::extractMetric(Node n, depthmapX::ColumnMatrix &metricPoints, + std::set &pixels, PointMap *pointdata, + const MetricTriple &curs) { + MetricPoint &cursMP = getMetricPoint(metricPoints, curs.pixel); + if (curs.dist == 0.0f || cursMP.m_point->blocked() || pointdata->blockedAdjacent(curs.pixel)) { + for (int i = 0; i < 32; i++) { + Bin &bin = n.bin(i); + for (auto pixVec : bin.m_pixel_vecs) { + for (PixelRef pix = pixVec.start(); pix.col(bin.m_dir) <= pixVec.end().col(bin.m_dir);) { + MetricPoint &mpt = getMetricPoint(metricPoints, pix); + // the nullptr check is unfortunately required because somehow depthmap stores + // neighbour pixels that are not really filled.. + if ((mpt.m_point != nullptr && mpt.m_unseen && + (mpt.m_dist == -1 || + (curs.dist + pointdata->getSpacing() * dist(pix, curs.pixel) < mpt.m_dist)))) { + mpt.m_dist = curs.dist + pointdata->getSpacing() * (float)dist(pix, curs.pixel); + pixels.insert(MetricTriple(mpt.m_dist, pix, curs.pixel)); + } + pix.move(bin.m_dir); + } + } + } + } +} diff --git a/modules/vgapaths/core/vgametricshortestpathtomany.h b/modules/vgapaths/core/vgametricshortestpathtomany.h new file mode 100644 index 00000000..274f2269 --- /dev/null +++ b/modules/vgapaths/core/vgametricshortestpathtomany.h @@ -0,0 +1,50 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// 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 3 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, see . + +#pragma once + +#include "salalib/ianalysis.h" +#include "salalib/options.h" +#include "salalib/pixelref.h" +#include "salalib/pointdata.h" + +class VGAMetricShortestPathToMany : public IAnalysis { + private: + PointMap &m_map; + const std::set m_pixelsFrom; + const std::set m_pixelsTo; + + struct MetricPoint { + Point *m_point = nullptr; + float m_linkCost = 0; + float m_dist = -1.0f; + float m_cumdist = -1.0f; + bool m_unseen = true; + }; + MetricPoint &getMetricPoint(depthmapX::ColumnMatrix &metricPoints, PixelRef ref) { + return (metricPoints(static_cast(ref.y), static_cast(ref.x))); + } + void extractMetric(Node n, depthmapX::ColumnMatrix &metricPoints, std::set &pixels, + PointMap *pointdata, const MetricTriple &curs); + + public: + std::string getAnalysisName() const override { return "Metric Shortest Path"; } + bool run(Communicator *) override; + VGAMetricShortestPathToMany(PointMap &map, std::set pixelsFrom, std::set pixelsTo) + : m_map(map), m_pixelsFrom(pixelsFrom), m_pixelsTo(pixelsTo) {} +}; diff --git a/modules/vgapaths/core/vgavisualshortestpath.cpp b/modules/vgapaths/core/vgavisualshortestpath.cpp new file mode 100644 index 00000000..9bf3f4f3 --- /dev/null +++ b/modules/vgapaths/core/vgavisualshortestpath.cpp @@ -0,0 +1,172 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// 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 3 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, see . + +#include "vgavisualshortestpath.h" + +#include "genlib/stringutils.h" + +bool VGAVisualShortestPath::run(Communicator *) { + + auto &attributes = m_map.getAttributeTable(); + + int path_col = attributes.insertOrResetColumn("Visual Shortest Path"); + int linked_col = attributes.insertOrResetColumn("Visual Shortest Path Linked"); + int order_col = attributes.insertOrResetColumn("Visual Shortest Path Order"); + int zone_col = attributes.insertOrResetColumn("Visual Shortest Path Zone"); + + for (auto &row : attributes) { + PixelRef pix = PixelRef(row.getKey().value); + Point &p = m_map.getPoint(pix); + p.m_misc = 0; + p.m_extent = pix; + } + + std::vector search_tree; + search_tree.push_back(PixelRefVector()); + + search_tree.back().push_back(m_pixelFrom); + + size_t level = 0; + std::map parents; + while (search_tree[level].size()) { + search_tree.push_back(PixelRefVector()); + auto &currLevelPix = search_tree[level]; + auto &nextLevelPix = search_tree[level + 1]; + for (auto iter = currLevelPix.rbegin(); iter != currLevelPix.rend(); ++iter) { + PixelRefVector newPixels; + PixelRefVector mergePixels; + Point &p = m_map.getPoint(*iter); + if (p.filled() && p.m_misc != ~0) { + if (!p.contextfilled() || iter->iseven() || level == 0) { + p.getNode().extractUnseen(newPixels, &m_map); + p.m_misc = ~0; + if (!p.getMergePixel().empty()) { + Point &p2 = m_map.getPoint(p.getMergePixel()); + if (p2.m_misc != ~0) { + newPixels.push_back(p.getMergePixel()); + p2.getNode().extractUnseen(mergePixels, &m_map); + for (auto &pixel : mergePixels) { + parents[pixel] = p.getMergePixel(); + } + p2.m_misc = ~0; + } + } + } else { + p.m_misc = ~0; + } + } + + for (auto &pixel : newPixels) { + parents[pixel] = *iter; + } + nextLevelPix.insert(nextLevelPix.end(), newPixels.begin(), newPixels.end()); + nextLevelPix.insert(nextLevelPix.end(), mergePixels.begin(), mergePixels.end()); + } + int linePixelCounter = 0; + for (auto iter = nextLevelPix.rbegin(); iter != nextLevelPix.rend(); ++iter) { + if (*iter == m_pixelTo) { + + for (auto &row : attributes) { + PixelRef pix = PixelRef(row.getKey().value); + Point &p = m_map.getPoint(pix); + p.m_misc = 0; + p.m_extent = pix; + } + + int counter = 0; + AttributeRow &lastPixelRow = attributes.getRow(AttributeKey(*iter)); + lastPixelRow.setValue(order_col, counter); + lastPixelRow.setValue(linked_col, 0); + counter++; + auto currParent = parents.find(*iter); + + while (currParent != parents.end()) { + Point &p = m_map.getPoint(currParent->second); + AttributeRow &row = attributes.getRow(AttributeKey(currParent->second)); + row.setValue(order_col, counter); + + if (!p.getMergePixel().empty() && p.getMergePixel() == currParent->first) { + row.setValue(linked_col, 1); + lastPixelRow.setValue(linked_col, 1); + } else { + // apparently we can't just have 1 number in the whole column + row.setValue(linked_col, 0); + auto pixelated = m_map.quickPixelateLine(currParent->first, currParent->second); + for (auto &linePixel : pixelated) { + auto *linePixelRow = attributes.getRowPtr(AttributeKey(linePixel)); + if (linePixelRow != 0) { + linePixelRow->setValue(path_col, linePixelCounter++); + + PixelRefVector newPixels; + Point &p = m_map.getPoint(linePixel); + p.getNode().extractUnseen(newPixels, &m_map); + for (auto &zonePixel : newPixels) { + auto *zonePixelRow = attributes.getRowPtr(AttributeKey(zonePixel)); + if (zonePixelRow != 0) { + if (zonePixelRow->getValue(zone_col) == -1) { + zonePixelRow->setValue(zone_col, linePixelCounter); + } + m_map.getPoint(zonePixel).m_misc = 0; + m_map.getPoint(zonePixel).m_extent = zonePixel; + } + } + } + } + } + + lastPixelRow = row; + currParent = parents.find(currParent->second); + + counter++; + } + + m_map.overrideDisplayedAttribute(-2); + m_map.setDisplayedAttribute(order_col); + + return true; + } + } + level++; + } + + return false; +} + +void VGAVisualShortestPath::extractUnseen(Node &node, PixelRefVector &pixels, depthmapX::RowMatrix &miscs, + depthmapX::RowMatrix &extents) { + for (int i = 0; i < 32; i++) { + Bin &bin = node.bin(i); + for (auto pixVec : bin.m_pixel_vecs) { + for (PixelRef pix = pixVec.start(); pix.col(bin.m_dir) <= pixVec.end().col(bin.m_dir);) { + int &misc = miscs(pix.y, pix.x); + PixelRef &extent = extents(pix.y, pix.x); + if (misc == 0) { + pixels.push_back(pix); + misc |= (1 << i); + } + // 10.2.02 revised --- diagonal was breaking this as it was extent in diagonal or horizontal + if (!(bin.m_dir & PixelRef::DIAGONAL)) { + if (extent.col(bin.m_dir) >= pixVec.end().col(bin.m_dir)) + break; + extent.col(bin.m_dir) = pixVec.end().col(bin.m_dir); + } + pix.move(bin.m_dir); + } + } + } +} diff --git a/modules/vgapaths/core/vgavisualshortestpath.h b/modules/vgapaths/core/vgavisualshortestpath.h new file mode 100644 index 00000000..2987fbd2 --- /dev/null +++ b/modules/vgapaths/core/vgavisualshortestpath.h @@ -0,0 +1,40 @@ +// sala - a component of the depthmapX - spatial network analysis platform +// Copyright (C) 2000-2010, University College London, Alasdair Turner +// Copyright (C) 2011-2012, Tasos Varoudis +// Copyright (C) 2017-2018, Petros Koutsolampros + +// 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 3 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, see . + +#pragma once + +#include "salalib/ianalysis.h" +#include "salalib/options.h" +#include "salalib/pixelref.h" +#include "salalib/pointdata.h" + +#include "genlib/simplematrix.h" + +class VGAVisualShortestPath : public IAnalysis { + private: + PointMap &m_map; + PixelRef m_pixelFrom, m_pixelTo; + void extractUnseen(Node &node, PixelRefVector &pixels, depthmapX::RowMatrix &miscs, + depthmapX::RowMatrix &extents); + + public: + std::string getAnalysisName() const override { return "Visibility Shortest Path"; } + bool run(Communicator *) override; + VGAVisualShortestPath(PointMap &map, PixelRef pixelFrom, PixelRef pixelTo) + : m_map(map), m_pixelFrom(pixelFrom), m_pixelTo(pixelTo) {} +}; diff --git a/modules/vgapaths/coreTest/CMakeLists.txt b/modules/vgapaths/coreTest/CMakeLists.txt new file mode 100644 index 00000000..59984458 --- /dev/null +++ b/modules/vgapaths/coreTest/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# 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 3 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, see . + +set(vgapathscoretest vgapathscoretest) +set(vgapathscoretest_SRCS + vgapathscoretest.cpp) + +set(modules_coreTest "${modules_coreTest}" "vgapathscoretest" CACHE INTERNAL "modules_coreTest" FORCE) + +add_compile_definitions(VGAPATHS_CORE_TEST_LIBRARY) + +add_library(${vgapathscoretest} OBJECT ${vgapathscoretest_SRCS}) diff --git a/modules/vgapaths/coreTest/vgapathscoretest.cpp b/modules/vgapaths/coreTest/vgapathscoretest.cpp new file mode 100644 index 00000000..3f4242d1 --- /dev/null +++ b/modules/vgapaths/coreTest/vgapathscoretest.cpp @@ -0,0 +1,18 @@ +// Copyright (C) 2020 Petros Koutsolampros + +// 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 3 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, see . + +#include "catch.hpp" + +TEST_CASE("Shortest paths examples", "") {} diff --git a/modules/vgapaths/gui/CMakeLists.txt b/modules/vgapaths/gui/CMakeLists.txt new file mode 100644 index 00000000..88329611 --- /dev/null +++ b/modules/vgapaths/gui/CMakeLists.txt @@ -0,0 +1,39 @@ +# Copyright (C) 2020 Petros Koutsolampros + +# 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 3 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, see . + +set(vgapathsgui vgapathsgui) +set(vgapathsgui_SRCS + vgapathsmainwindow.cpp) +set(modules_gui "${modules_gui}" "vgapathsgui" CACHE INTERNAL "modules_gui" FORCE) + +find_package(Qt5 COMPONENTS Core Widgets Gui OpenGL REQUIRED) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${Qt5Core_INCLUDE_DIRS}) +include_directories(${Qt5Widgets_INCLUDE_DIRS}) +include_directories(${Qt5Gui_INCLUDE_DIRS}) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOUIC_SEARCH_PATHS "../../../depthmapX/UI") + +add_definitions(${Qt5Core_DEFINITIONS}) +add_definitions(${Qt5Widgets_DEFINITIONS}) +add_definitions(${Qt5Gui_DEFINITIONS}) + +add_compile_definitions(VGAPATHS_GUI_LIBRARY) + +add_library(${vgapathsgui} OBJECT ${vgapathsgui_SRCS}) diff --git a/modules/vgapaths/gui/vgapathsmainwindow.cpp b/modules/vgapaths/gui/vgapathsmainwindow.cpp new file mode 100644 index 00000000..d1cc3e5e --- /dev/null +++ b/modules/vgapaths/gui/vgapathsmainwindow.cpp @@ -0,0 +1,201 @@ +// Copyright (C) 2020 Petros Koutsolampros + +// 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 3 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, see . + +#include "vgapathsmainwindow.h" + +#include "modules/vgapaths/core/extractlinkdata.h" +#include "modules/vgapaths/core/vgaangularshortestpath.h" +#include "modules/vgapaths/core/vgaisovistzone.h" +#include "modules/vgapaths/core/vgametricdepthlinkcost.h" +#include "modules/vgapaths/core/vgametricshortestpath.h" +#include "modules/vgapaths/core/vgametricshortestpathtomany.h" +#include "modules/vgapaths/core/vgavisualshortestpath.h" + +#include "depthmapX/mainwindowhelpers.h" + +#include +#include + +bool VGAPathsMainWindow::createMenus(MainWindow *mainWindow) { + QMenu *toolsMenu = MainWindowHelpers::getOrAddRootMenu(mainWindow, tr("&Tools")); + QMenu *visibilitySubMenu = MainWindowHelpers::getOrAddMenu(toolsMenu, tr("&Visibility")); + QMenu *shortestPathSubMenu = MainWindowHelpers::getOrAddMenu(visibilitySubMenu, tr("Shortest Paths")); + + QAction *visibilityShortestPathAct = new QAction(tr("Visibility Shortest Path"), this); + visibilityShortestPathAct->setStatusTip(tr("Shortest visual path between two selected points")); + connect(visibilityShortestPathAct, &QAction::triggered, this, + [this, mainWindow] { OnShortestPath(mainWindow, PathType::VISUAL); }); + shortestPathSubMenu->addAction(visibilityShortestPathAct); + + QAction *metricShortestPathAct = new QAction(tr("&Metric Shortest Path"), this); + metricShortestPathAct->setStatusTip(tr("Shortest metric path between two selected points")); + connect(metricShortestPathAct, &QAction::triggered, this, + [this, mainWindow] { OnShortestPath(mainWindow, PathType::METRIC); }); + shortestPathSubMenu->addAction(metricShortestPathAct); + + QAction *angularShortestPathAct = new QAction(tr("&Angular Shortest Path"), this); + angularShortestPathAct->setStatusTip(tr("Shortest angular path between two selected points")); + connect(angularShortestPathAct, &QAction::triggered, this, + [this, mainWindow] { OnShortestPath(mainWindow, PathType::ANGULAR); }); + shortestPathSubMenu->addAction(angularShortestPathAct); + + QAction *extractLinkDataAct = new QAction(tr("&Extract Link Data"), this); + extractLinkDataAct->setStatusTip(tr("Extracts data from the links and adds them to the attribute table")); + connect(extractLinkDataAct, &QAction::triggered, this, [this, mainWindow] { OnExtractLinkData(mainWindow); }); + visibilitySubMenu->addAction(extractLinkDataAct); + + return true; +} + +void VGAPathsMainWindow::OnShortestPath(MainWindow *mainWindow, PathType pathType) { + QGraphDoc *graphDoc = mainWindow->activeMapDoc(); + if (graphDoc == nullptr) + return; + + if (graphDoc->m_communicator) { + QMessageBox::warning(mainWindow, tr("Warning"), tr("Please wait, another process is running"), QMessageBox::Ok, + QMessageBox::Ok); + return; + } + if (graphDoc->m_meta_graph->getDisplayedMapType() != ShapeMap::POINTMAP) { + QMessageBox::warning(mainWindow, tr("Warning"), tr("Please make sure the displayed map is a VGA map"), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + PointMap &pointMap = graphDoc->m_meta_graph->getDisplayedPointMap(); + if (pointMap.getSelSet().size() != 2) { + QMessageBox::warning(mainWindow, tr("Warning"), tr("Please select two cells to create a path between"), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + const PixelRef &pixelFrom = *pointMap.getSelSet().begin(); + const PixelRef &pixelTo = *std::next(pointMap.getSelSet().begin()); + + graphDoc->m_communicator = new CMSCommunicator(); + switch (pathType) { + case PathType::VISUAL: + graphDoc->m_communicator->setAnalysis( + std::unique_ptr(new VGAVisualShortestPath(pointMap, pixelFrom, pixelTo))); + break; + case PathType::METRIC: { + std::set pixelsFrom; + pixelsFrom.insert(pixelFrom); + graphDoc->m_communicator->setAnalysis( + std::unique_ptr(new VGAMetricShortestPath(pointMap, pixelsFrom, pixelTo))); + break; + } + case PathType::ANGULAR: + graphDoc->m_communicator->setAnalysis( + std::unique_ptr(new VGAAngularShortestPath(pointMap, pixelFrom, pixelTo))); + break; + } + graphDoc->m_communicator->SetFunction(CMSCommunicator::FROMCONNECTOR); + graphDoc->m_communicator->setSuccessUpdateFlags(QGraphDoc::NEW_DATA); + graphDoc->m_communicator->setSuccessRedrawFlags(QGraphDoc::VIEW_ALL, QGraphDoc::REDRAW_POINTS, + QGraphDoc::NEW_DATA); + + graphDoc->CreateWaitDialog(tr("Calculating shortest path...")); + graphDoc->m_thread.render(graphDoc); +} + +void VGAPathsMainWindow::OnExtractLinkData(MainWindow *mainWindow) { + QGraphDoc *graphDoc = mainWindow->activeMapDoc(); + if (graphDoc == nullptr) + return; + + if (graphDoc->m_communicator) { + QMessageBox::warning(mainWindow, tr("Warning"), tr("Please wait, another process is running"), QMessageBox::Ok, + QMessageBox::Ok); + return; + } + if (graphDoc->m_meta_graph->getDisplayedMapType() != ShapeMap::POINTMAP) { + QMessageBox::warning(mainWindow, tr("Warning"), tr("Please make sure the displayed map is a VGA map"), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + + graphDoc->m_communicator->setAnalysis( + std::unique_ptr(new ExtractLinkData(graphDoc->m_meta_graph->getDisplayedPointMap()))); + + graphDoc->m_communicator->SetFunction(CMSCommunicator::FROMCONNECTOR); + graphDoc->m_communicator->setSuccessUpdateFlags(QGraphDoc::NEW_DATA); + graphDoc->m_communicator->setSuccessRedrawFlags(QGraphDoc::VIEW_ALL, QGraphDoc::REDRAW_POINTS, + QGraphDoc::NEW_DATA); + + graphDoc->CreateWaitDialog(tr("Extracting link data..")); + graphDoc->m_thread.render(graphDoc); +} + +void VGAPathsMainWindow::OnMakeIsovistZones(MainWindow *mainWindow) { + QGraphDoc *graphDoc = mainWindow->activeMapDoc(); + if (graphDoc == nullptr) + return; + + if (graphDoc->m_communicator) { + QMessageBox::warning(mainWindow, tr("Warning"), tr("Please wait, another process is running"), QMessageBox::Ok, + QMessageBox::Ok); + return; + } + if (graphDoc->m_meta_graph->getDisplayedMapType() != ShapeMap::POINTMAP) { + QMessageBox::warning(mainWindow, tr("Warning"), tr("Please make sure the displayed map is a VGA map"), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + + std::map> originPointSets; + float restrictDistance = -1; + + graphDoc->m_communicator->setAnalysis(std::unique_ptr( + new VGAIsovistZone(graphDoc->m_meta_graph->getDisplayedPointMap(), originPointSets, restrictDistance))); + + graphDoc->m_communicator->SetFunction(CMSCommunicator::FROMCONNECTOR); + graphDoc->m_communicator->setSuccessUpdateFlags(QGraphDoc::NEW_DATA); + graphDoc->m_communicator->setSuccessRedrawFlags(QGraphDoc::VIEW_ALL, QGraphDoc::REDRAW_POINTS, + QGraphDoc::NEW_DATA); + + graphDoc->CreateWaitDialog(tr("Making isovist zones..")); + graphDoc->m_thread.render(graphDoc); +} + +void VGAPathsMainWindow::OnMetricShortestPathsToMany(MainWindow *mainWindow) { + QGraphDoc *graphDoc = mainWindow->activeMapDoc(); + if (graphDoc == nullptr) + return; + + if (graphDoc->m_communicator) { + QMessageBox::warning(mainWindow, tr("Warning"), tr("Please wait, another process is running"), QMessageBox::Ok, + QMessageBox::Ok); + return; + } + if (graphDoc->m_meta_graph->getDisplayedMapType() != ShapeMap::POINTMAP) { + QMessageBox::warning(mainWindow, tr("Warning"), tr("Please make sure the displayed map is a VGA map"), + QMessageBox::Ok, QMessageBox::Ok); + return; + } + + std::set pixelsFrom; + std::set pixelsTo; + + graphDoc->m_communicator->setAnalysis(std::unique_ptr( + new VGAMetricShortestPathToMany(graphDoc->m_meta_graph->getDisplayedPointMap(), pixelsFrom, pixelsTo))); + + graphDoc->m_communicator->SetFunction(CMSCommunicator::FROMCONNECTOR); + graphDoc->m_communicator->setSuccessUpdateFlags(QGraphDoc::NEW_DATA); + graphDoc->m_communicator->setSuccessRedrawFlags(QGraphDoc::VIEW_ALL, QGraphDoc::REDRAW_POINTS, + QGraphDoc::NEW_DATA); + + graphDoc->CreateWaitDialog(tr("Making isovist zones..")); + graphDoc->m_thread.render(graphDoc); +} diff --git a/modules/vgapaths/gui/vgapathsmainwindow.h b/modules/vgapaths/gui/vgapathsmainwindow.h new file mode 100644 index 00000000..4b0e0634 --- /dev/null +++ b/modules/vgapaths/gui/vgapathsmainwindow.h @@ -0,0 +1,34 @@ +// Copyright (C) 2020 Petros Koutsolampros + +// 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 3 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, see . + +#pragma once + +#include "depthmapX/imainwindowmodule.h" + +class VGAPathsMainWindow : public IMainWindowModule { + + private: + enum PathType { VISUAL, ANGULAR, METRIC }; + + private slots: + void OnShortestPath(MainWindow *mainWindow, PathType pathType); + void OnExtractLinkData(MainWindow *mainWindow); + void OnMakeIsovistZones(MainWindow *mainWindow); + void OnMetricShortestPathsToMany(MainWindow *mainWindow); + + public: + VGAPathsMainWindow() : IMainWindowModule() {} + bool createMenus(MainWindow *mainWindow); +}; diff --git a/salalib/entityparsing.cpp b/salalib/entityparsing.cpp index 7d0642a9..030114fa 100644 --- a/salalib/entityparsing.cpp +++ b/salalib/entityparsing.cpp @@ -188,6 +188,91 @@ namespace EntityParsing { return points; } + std::pair, std::vector> parsePointSets(std::istream& stream, char delimiter) { + + std::pair, std::vector> pointSets; + + std::string inputline; + std::getline(stream, inputline); + + std::vector strings = dXstring::split(inputline, delimiter); + + if (strings.size() < 2) + { + throw EntityParseException("Badly formatted header (should contain x and y)"); + } + + size_t i; + for (i = 0; i < strings.size(); i++) + { + if (!strings[i].empty()) + { + std::transform(strings[i].begin(), strings[i].end(), strings[i].begin(), ::tolower); + //strings[i].ltrim('\"'); + //strings[i].rtrim('\"'); + } + } + + int xcol = -1, ycol = -1, setcol = -1; + for (i = 0; i < strings.size(); i++) { + if (strings[i] == "x") + { + xcol = i; + } + else if (strings[i] == "y") + { + ycol = i; + } + else if (strings[i] == "set") + { + setcol = i; + } + } + + if(xcol == -1 || ycol == -1) + { + throw EntityParseException("Badly formatted header (should contain x and y)"); + } + + Point2f p; + std::string setName = ""; + + while (!stream.eof()) { + std::getline(stream, inputline); + if (!inputline.empty()) { + strings = dXstring::split(inputline, delimiter); + if (!strings.size()) + { + continue; + } + if (strings.size() < 2) + { + std::stringstream message; + message << "Error parsing line: " << inputline << std::flush; + throw EntityParseException(message.str().c_str()); + } + for (i = 0; i < strings.size(); i++) + { + if (i == xcol) + { + p.x = std::atof(strings[i].c_str()); + } + else if (i == ycol) + { + p.y = std::atof(strings[i].c_str()); + } + else if (i == setcol) + { + setName = strings[i].c_str(); + } + } + pointSets.first.push_back(setName); + pointSets.second.push_back(p); + } + } + return pointSets; + } + Point2f parsePoint(const std::string &point, char delimiter) { std::vector strings = dXstring::split(point, delimiter); diff --git a/salalib/entityparsing.h b/salalib/entityparsing.h index 4d60e833..62e60a40 100644 --- a/salalib/entityparsing.h +++ b/salalib/entityparsing.h @@ -35,6 +35,7 @@ namespace EntityParsing { std::vector split(const std::string &s, char delim); std::vector parseLines(std::istream& stream, char delimiter); std::vector parsePoints(std::istream& stream, char delimiter); + std::pair, std::vector> parsePointSets(std::istream& stream, char delimiter = '\t'); Point2f parsePoint(const std::string &point, char delimiter = ','); std::vector parseIsovists(std::istream &stream, char delimiter); IsovistDefinition parseIsovist(const std::string &isovist); From 3edbd9daa1c889d7bf39cf3c8a703d3f85b59be2 Mon Sep 17 00:00:00 2001 From: Petros Koutsolampros Date: Tue, 29 Dec 2020 23:51:53 +0200 Subject: [PATCH 2/4] Add the UIC trigger to the module --- modules/vgapaths/gui/CMakeLists.txt | 1 + modules/vgapaths/gui/uictrigger.cpp | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 modules/vgapaths/gui/uictrigger.cpp diff --git a/modules/vgapaths/gui/CMakeLists.txt b/modules/vgapaths/gui/CMakeLists.txt index 88329611..792c9b7e 100644 --- a/modules/vgapaths/gui/CMakeLists.txt +++ b/modules/vgapaths/gui/CMakeLists.txt @@ -15,6 +15,7 @@ set(vgapathsgui vgapathsgui) set(vgapathsgui_SRCS + uictrigger.cpp vgapathsmainwindow.cpp) set(modules_gui "${modules_gui}" "vgapathsgui" CACHE INTERNAL "modules_gui" FORCE) diff --git a/modules/vgapaths/gui/uictrigger.cpp b/modules/vgapaths/gui/uictrigger.cpp new file mode 100644 index 00000000..3d79019a --- /dev/null +++ b/modules/vgapaths/gui/uictrigger.cpp @@ -0,0 +1,5 @@ +// This file is required to trigger cmake's AUTOUIC in the depthmapX directory +// if it is not included then the ui_*.h files in that directory are not generated +// and then not found by the module which is built first because it is a dependency +// of depthmapX. +#include "depthmapX/ui_ColourScaleDlg.h" From 6d07e5b744507ce42cbb27182685d7070bbfe5e0 Mon Sep 17 00:00:00 2001 From: Petros Koutsolampros Date: Wed, 30 Dec 2020 00:05:47 +0200 Subject: [PATCH 3/4] Make sure the newly created column shown is the path --- modules/vgapaths/core/vgaangularshortestpath.cpp | 2 +- modules/vgapaths/core/vgametricshortestpath.cpp | 2 +- modules/vgapaths/core/vgavisualshortestpath.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/vgapaths/core/vgaangularshortestpath.cpp b/modules/vgapaths/core/vgaangularshortestpath.cpp index e7d8d59e..fda2d6dd 100644 --- a/modules/vgapaths/core/vgaangularshortestpath.cpp +++ b/modules/vgapaths/core/vgaangularshortestpath.cpp @@ -146,7 +146,7 @@ bool VGAAngularShortestPath::run(Communicator *) { } m_map.overrideDisplayedAttribute(-2); - m_map.setDisplayedAttribute(order_col); + m_map.setDisplayedAttribute(path_col); return true; } diff --git a/modules/vgapaths/core/vgametricshortestpath.cpp b/modules/vgapaths/core/vgametricshortestpath.cpp index 7f485b3e..ad225a31 100644 --- a/modules/vgapaths/core/vgametricshortestpath.cpp +++ b/modules/vgapaths/core/vgametricshortestpath.cpp @@ -144,7 +144,7 @@ bool VGAMetricShortestPath::run(Communicator *) { } m_map.overrideDisplayedAttribute(-2); - m_map.setDisplayedAttribute(order_col); + m_map.setDisplayedAttribute(path_col); return true; } diff --git a/modules/vgapaths/core/vgavisualshortestpath.cpp b/modules/vgapaths/core/vgavisualshortestpath.cpp index 9bf3f4f3..16f3b4e7 100644 --- a/modules/vgapaths/core/vgavisualshortestpath.cpp +++ b/modules/vgapaths/core/vgavisualshortestpath.cpp @@ -136,7 +136,7 @@ bool VGAVisualShortestPath::run(Communicator *) { } m_map.overrideDisplayedAttribute(-2); - m_map.setDisplayedAttribute(order_col); + m_map.setDisplayedAttribute(path_col); return true; } From 2c4010a14ee6b72873c9ed99b827a904bffed292 Mon Sep 17 00:00:00 2001 From: Petros Koutsolampros Date: Wed, 30 Dec 2020 00:06:13 +0200 Subject: [PATCH 4/4] Add some regression tests for the vgapaths module --- .../RegressionTest/regressionconfig.json | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 modules/vgapaths/RegressionTest/regressionconfig.json diff --git a/modules/vgapaths/RegressionTest/regressionconfig.json b/modules/vgapaths/RegressionTest/regressionconfig.json new file mode 100644 index 00000000..f9eb4700 --- /dev/null +++ b/modules/vgapaths/RegressionTest/regressionconfig.json @@ -0,0 +1,37 @@ +{ + "rundir": "rundir", + "basebinlocation": "../../BaselineBinaries", + "testbinlocation": "../../../build", + "testcases": { + "vga_shortest_metric_path": [{ + "infile": "../../../testdata/gallery_connected.graph", + "outfile": "out.graph", + "mode": "VGASHORTESTPATH", + "extraArgs": { + "-spo":"0.8,7.7", + "-spd":"5,5.4", + "-spt":"metric" + } + }], + "vga_shortest_angular_path": [{ + "infile": "../../../testdata/gallery_connected.graph", + "outfile": "out.graph", + "mode": "VGASHORTESTPATH", + "extraArgs": { + "-spo":"0.8,7.7", + "-spd":"5,5.4", + "-spt":"angular" + } + }], + "vga_shortest_visual_path": [{ + "infile": "../../../testdata/gallery_connected.graph", + "outfile": "out.graph", + "mode": "VGASHORTESTPATH", + "extraArgs": { + "-spo":"0.8,7.7", + "-spd":"5,5.4", + "-spt":"visual" + } + }] + } +}