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/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"
+ }
+ }]
+ }
+}
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..fda2d6dd
--- /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(path_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..ad225a31
--- /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(path_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..16f3b4e7
--- /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(path_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..792c9b7e
--- /dev/null
+++ b/modules/vgapaths/gui/CMakeLists.txt
@@ -0,0 +1,40 @@
+# 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
+ uictrigger.cpp
+ 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/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"
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);