Skip to content

Commit a25d9b1

Browse files
authored
Merge branch 'master' into test_fix_shape_removal
2 parents 7e4fea3 + e3877a6 commit a25d9b1

File tree

11 files changed

+245
-53
lines changed

11 files changed

+245
-53
lines changed

.mailmap

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Petros Koutsolampros <2184600+pklampros@users.noreply.github.com> <2184600+orange-vertex@users.noreply.github.com>
2+
Petros Koutsolampros <2184600+pklampros@users.noreply.github.com> <orange-vertex@users.noreply.github.com>
3+

salaTest/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ set(salaTest_SRCS
2121
testmapinfodata.cpp
2222
testsalaprogram.cpp
2323
testdxfp.cpp
24-
testshaperemove.cpp)
24+
testshaperemove.cpp
25+
testmapconversion.cpp)
2526

2627
include_directories("../ThirdParty/Catch" "../ThirdParty/FakeIt")
2728

salaTest/testmapconversion.cpp

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
// Copyright (C) 2020 Petros Koutsolampros
2+
3+
// This program is free software: you can redistribute it and/or modify
4+
// it under the terms of the GNU General Public License as published by
5+
// the Free Software Foundation, either version 3 of the License, or
6+
// (at your option) any later version.
7+
8+
// This program is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU General Public License for more details.
12+
13+
// You should have received a copy of the GNU General Public License
14+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
16+
#include "catch.hpp"
17+
#include "salalib/mapconverter.h"
18+
#include "salalib/mgraph.h"
19+
20+
TEST_CASE("Failing empty drawing map conversion", "") {
21+
std::vector<SpacePixelFile> drawingFiles;
22+
REQUIRE_THROWS_WITH(MapConverter::convertDrawingToAxial(nullptr, "Axial map", drawingFiles),
23+
Catch::Contains("Failed to convert lines"));
24+
REQUIRE_THROWS_WITH(MapConverter::convertDrawingToSegment(nullptr, "Segment map", drawingFiles),
25+
Catch::Contains("No lines found in drawing"));
26+
REQUIRE_THROWS_WITH(MapConverter::convertDrawingToConvex(nullptr, "Convex map", drawingFiles),
27+
Catch::Contains("No polygons found in drawing"));
28+
29+
drawingFiles.push_back(SpacePixelFile("Drawing file"));
30+
REQUIRE_THROWS_WITH(MapConverter::convertDrawingToAxial(nullptr, "Axial map", drawingFiles),
31+
Catch::Contains("Failed to convert lines"));
32+
REQUIRE_THROWS_WITH(MapConverter::convertDrawingToSegment(nullptr, "Segment map", drawingFiles),
33+
Catch::Contains("No lines found in drawing"));
34+
REQUIRE_THROWS_WITH(MapConverter::convertDrawingToConvex(nullptr, "Convex map", drawingFiles),
35+
Catch::Contains("No polygons found in drawing"));
36+
37+
drawingFiles.back().m_spacePixels.push_back(ShapeMap("Drawing layer", ShapeMap::DRAWINGMAP));
38+
REQUIRE_THROWS_WITH(MapConverter::convertDrawingToAxial(nullptr, "Axial map", drawingFiles),
39+
Catch::Contains("Failed to convert lines"));
40+
REQUIRE_THROWS_WITH(MapConverter::convertDrawingToSegment(nullptr, "Segment map", drawingFiles),
41+
Catch::Contains("No lines found in drawing"));
42+
REQUIRE_THROWS_WITH(MapConverter::convertDrawingToConvex(nullptr, "Convex map", drawingFiles),
43+
Catch::Contains("No polygons found in drawing"));
44+
}
45+
46+
TEST_CASE("Failing empty axial to segment map conversion", "") {
47+
ShapeGraph segmentMap("Axial map", ShapeMap::AXIALMAP);
48+
// TODO: Does not throw an exception but maybe it should as the axial map is empty?
49+
// REQUIRE_THROWS_WITH(MapConverter::convertAxialToSegment(nullptr, segmentMap, "Segment map", false, false, 0),
50+
// Catch::Contains("No lines found in drawing"));
51+
}
52+
53+
TEST_CASE("Failing empty data map conversion", "") {
54+
ShapeMap dataMap("Data map", ShapeMap::DATAMAP);
55+
REQUIRE_THROWS_WITH(MapConverter::convertDataToAxial(nullptr, "Axial map", dataMap),
56+
Catch::Contains("No lines found in data map"));
57+
REQUIRE_THROWS_WITH(MapConverter::convertDataToSegment(nullptr, "Segment map", dataMap),
58+
Catch::Contains("No lines found in data map"));
59+
REQUIRE_THROWS_WITH(MapConverter::convertDataToConvex(nullptr, "Convex map", dataMap),
60+
Catch::Contains("No polygons found in data map"));
61+
}
62+
63+
TEST_CASE("Test drawing to segment conversion", "") {
64+
const float EPSILON = 0.001;
65+
66+
Line line1(Point2f(0, 0), Point2f(0, 1));
67+
Line line2(Point2f(0, 1), Point2f(1, 1));
68+
Line line3(Point2f(1, 1), Point2f(1, 0));
69+
70+
std::vector<SpacePixelFile> drawingFiles;
71+
drawingFiles.push_back(SpacePixelFile("Drawing file"));
72+
drawingFiles.back().m_spacePixels.push_back(ShapeMap("Drawing layer", ShapeMap::DRAWINGMAP));
73+
ShapeMap &drawingLayer = drawingFiles.back().m_spacePixels.back();
74+
75+
SECTION("Single line") {
76+
drawingLayer.makeLineShape(line1);
77+
78+
// TODO: This fails with std::bad_alloc because there's only 1 line in the drawing
79+
REQUIRE_THROWS_WITH(MapConverter::convertDrawingToSegment(nullptr, "Segment map", drawingFiles),
80+
Catch::Contains("std::bad_alloc"));
81+
}
82+
83+
SECTION("Two lines") {
84+
drawingLayer.makeLineShape(line1);
85+
drawingLayer.makeLineShape(line2);
86+
87+
// TODO: This fails with std::bad_alloc because there's only 2 lines in the drawing
88+
REQUIRE_THROWS_WITH(MapConverter::convertDrawingToSegment(nullptr, "Segment map", drawingFiles),
89+
Catch::Contains("std::bad_alloc"));
90+
}
91+
92+
SECTION("Three lines") {
93+
drawingLayer.makeLineShape(line1);
94+
drawingLayer.makeLineShape(line2);
95+
drawingLayer.makeLineShape(line3);
96+
std::unique_ptr<ShapeGraph> segmentMap =
97+
MapConverter::convertDrawingToSegment(nullptr, "Segment map", drawingFiles);
98+
std::map<int, SalaShape> &shapes = segmentMap->getAllShapes();
99+
REQUIRE(shapes.size() == 3);
100+
auto shapeIter = shapes.begin();
101+
REQUIRE(shapeIter->first == 0);
102+
const Line &segmentLine1 = shapeIter->second.getLine();
103+
REQUIRE(segmentLine1.ax() == Approx(line1.ax()).epsilon(EPSILON));
104+
REQUIRE(segmentLine1.ay() == Approx(line1.ay()).epsilon(EPSILON));
105+
REQUIRE(segmentLine1.bx() == Approx(line1.bx()).epsilon(EPSILON));
106+
REQUIRE(segmentLine1.by() == Approx(line1.by()).epsilon(EPSILON));
107+
shapeIter++;
108+
REQUIRE(shapeIter->first == 1);
109+
const Line &segmentLine2 = shapeIter->second.getLine();
110+
REQUIRE(segmentLine2.ax() == Approx(line2.ax()).epsilon(EPSILON));
111+
REQUIRE(segmentLine2.ay() == Approx(line2.ay()).epsilon(EPSILON));
112+
REQUIRE(segmentLine2.bx() == Approx(line2.bx()).epsilon(EPSILON));
113+
REQUIRE(segmentLine2.by() == Approx(line2.by()).epsilon(EPSILON));
114+
shapeIter++;
115+
REQUIRE(shapeIter->first == 2);
116+
const Line &segmentLine3 = shapeIter->second.getLine();
117+
REQUIRE(segmentLine3.ax() == Approx(line3.ax()).epsilon(EPSILON));
118+
REQUIRE(segmentLine3.ay() == Approx(line3.ay()).epsilon(EPSILON));
119+
REQUIRE(segmentLine3.bx() == Approx(line3.bx()).epsilon(EPSILON));
120+
REQUIRE(segmentLine3.by() == Approx(line3.by()).epsilon(EPSILON));
121+
}
122+
}
123+
124+
TEST_CASE("Test data to segment conversion", "") {
125+
const float EPSILON = 0.001;
126+
127+
std::string newAttributeName = "testID";
128+
ShapeMap dataMap("Data map", ShapeMap::DATAMAP);
129+
int newAttributeID = dataMap.addAttribute(newAttributeName);
130+
131+
std::vector<Line> lines;
132+
std::vector<std::map<int, float>> extraAttributes;
133+
134+
lines.push_back(Line(Point2f(0, 0), Point2f(0, 1)));
135+
lines.push_back(Line(Point2f(0, 1), Point2f(1, 1)));
136+
lines.push_back(Line(Point2f(1, 1), Point2f(1, 0)));
137+
138+
for (int i = 0; i < lines.size(); i++) {
139+
extraAttributes.push_back(std::map<int, float>());
140+
extraAttributes.back()[newAttributeID] = extraAttributes.size();
141+
}
142+
143+
SECTION("Single line with extra attributes") {
144+
dataMap.makeLineShape(lines[0], false, false, extraAttributes[0]);
145+
146+
// TODO: This fails with std::bad_alloc because there's only 1 line in the data map
147+
REQUIRE_THROWS_WITH(MapConverter::convertDataToSegment(nullptr, "Segment map", dataMap, true),
148+
Catch::Contains("std::bad_alloc"));
149+
}
150+
151+
SECTION("Two lines with extra attributes") {
152+
dataMap.makeLineShape(lines[0], false, false, extraAttributes[0]);
153+
dataMap.makeLineShape(lines[1], false, false, extraAttributes[1]);
154+
155+
// TODO: This fails with std::bad_alloc because there's only 2 lines in the data map
156+
REQUIRE_THROWS_WITH(MapConverter::convertDataToSegment(nullptr, "Segment map", dataMap, true),
157+
Catch::Contains("std::bad_alloc"));
158+
}
159+
160+
SECTION("Three lines") {
161+
dataMap.makeLineShape(lines[0], false, false, extraAttributes[0]);
162+
dataMap.makeLineShape(lines[1], false, false, extraAttributes[1]);
163+
dataMap.makeLineShape(lines[2], false, false, extraAttributes[2]);
164+
std::unique_ptr<ShapeGraph> segmentMap =
165+
MapConverter::convertDataToSegment(nullptr, "Segment map", dataMap, true);
166+
int segmentNewAttributeID = segmentMap->getAttributeTable().getColumnIndex(newAttributeName);
167+
std::map<int, SalaShape> &shapes = segmentMap->getAllShapes();
168+
REQUIRE(shapes.size() == 3);
169+
auto shapeIter = shapes.begin();
170+
for (int i = 0; i < lines.size(); i++) {
171+
REQUIRE(shapeIter->first == i);
172+
AttributeRow &row = segmentMap->getAttributeRowFromShapeIndex(shapeIter->first);
173+
REQUIRE(row.getValue(segmentNewAttributeID) == extraAttributes[i][newAttributeID]);
174+
const Line &segmentLine = shapeIter->second.getLine();
175+
REQUIRE(segmentLine.ax() == Approx(lines[i].ax()).epsilon(EPSILON));
176+
REQUIRE(segmentLine.ay() == Approx(lines[i].ay()).epsilon(EPSILON));
177+
REQUIRE(segmentLine.bx() == Approx(lines[i].bx()).epsilon(EPSILON));
178+
REQUIRE(segmentLine.by() == Approx(lines[i].by()).epsilon(EPSILON));
179+
shapeIter++;
180+
}
181+
}
182+
183+
SECTION("Four lines, second line twice") {
184+
dataMap.makeLineShape(lines[0], false, false, extraAttributes[0]);
185+
dataMap.makeLineShape(lines[1], false, false, extraAttributes[1]);
186+
dataMap.makeLineShape(lines[1], false, false, extraAttributes[1]); // this one should be removed by tidylines
187+
dataMap.makeLineShape(lines[2], false, false, extraAttributes[2]);
188+
std::unique_ptr<ShapeGraph> segmentMap =
189+
MapConverter::convertDataToSegment(nullptr, "Segment map", dataMap, true);
190+
int segmentNewAttributeID = segmentMap->getAttributeTable().getColumnIndex(newAttributeName);
191+
std::map<int, SalaShape> &shapes = segmentMap->getAllShapes();
192+
REQUIRE(shapes.size() == 3);
193+
auto shapeIter = shapes.begin();
194+
for (int i = 0; i < lines.size(); i++) {
195+
REQUIRE(shapeIter->first == i);
196+
AttributeRow &row = segmentMap->getAttributeRowFromShapeIndex(shapeIter->first);
197+
REQUIRE(row.getValue(segmentNewAttributeID) == extraAttributes[i][newAttributeID]);
198+
const Line &segmentLine = shapeIter->second.getLine();
199+
REQUIRE(segmentLine.ax() == Approx(lines[i].ax()).epsilon(EPSILON));
200+
REQUIRE(segmentLine.ay() == Approx(lines[i].ay()).epsilon(EPSILON));
201+
REQUIRE(segmentLine.bx() == Approx(lines[i].bx()).epsilon(EPSILON));
202+
REQUIRE(segmentLine.by() == Approx(lines[i].by()).epsilon(EPSILON));
203+
shapeIter++;
204+
}
205+
}
206+
}

salalib/axialmap.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,6 @@ bool ShapeGraph::read(std::istream &stream)
182182
{
183183
m_attributes->clear();
184184
m_connectors.clear();
185-
m_selection = false;
186185
m_map_type = ShapeMap::EMPTYMAP;
187186

188187
// note that keyvertexcount and keyvertices are different things! (length keyvertices not the same as keyvertexcount!)

salalib/mapconverter.cpp

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ std::unique_ptr<ShapeGraph> MapConverter::convertDrawingToAxial(Communicator *co
1616
}
1717

1818
QtRegion region;
19-
std::map<int,Line> lines; // map required for tidy lines, otherwise acts like vector
20-
std::map<int,int> layers; // this is used to say which layer it originated from
19+
std::map<int, std::pair<Line, int>> lines; // map required for tidy lines, otherwise acts like vector
20+
// this is used to say which layer it originated from
2121

2222
bool recordlayer = false;
2323

@@ -35,8 +35,7 @@ std::unique_ptr<ShapeGraph> MapConverter::convertDrawingToAxial(Communicator *co
3535
}
3636
std::vector<SimpleLine> newLines = pixel.getAllShapesAsLines();
3737
for (const auto& line: newLines) {
38-
lines.insert(std::make_pair(count, Line(line.start(), line.end())));
39-
layers.insert(std::make_pair(count,j));
38+
lines.insert(std::make_pair(count, std::make_pair(Line(line.start(), line.end()), j)));
4039
count ++;
4140
}
4241
pixel.setShow(false);
@@ -77,9 +76,9 @@ std::unique_ptr<ShapeGraph> MapConverter::convertDrawingToAxial(Communicator *co
7776
for (auto & line: lines) {
7877
if (recordlayer)
7978
{
80-
layerAttributes[layerCol] = float(layers.find(line.first)->second);
79+
layerAttributes[layerCol] = float(line.second.second);
8180
}
82-
usermap->makeLineShape(line.second, false, false, layerAttributes );
81+
usermap->makeLineShape(line.second.first, false, false, layerAttributes );
8382
}
8483

8584
usermap->makeConnections();
@@ -100,8 +99,7 @@ std::unique_ptr<ShapeGraph> MapConverter::convertDataToAxial(Communicator *comm,
10099

101100
// add all visible layers to the set of polygon lines...
102101

103-
std::map<int,Line> lines;
104-
std::map<int,int> keys;
102+
std::map<int, std::pair<Line, int>> lines;
105103

106104
//m_region = shapemap.getRegion();
107105
QtRegion region = shapemap.getRegion();
@@ -114,8 +112,7 @@ std::unique_ptr<ShapeGraph> MapConverter::convertDataToAxial(Communicator *comm,
114112

115113
std::vector<Line> shapeLines = shape.second.getAsLines();
116114
for(Line line: shapeLines) {
117-
lines.insert(std::make_pair(count,line));
118-
keys.insert(std::make_pair(count,key));
115+
lines.insert(std::make_pair(count, std::make_pair(line, key)));
119116
count++;
120117
}
121118
}
@@ -178,17 +175,15 @@ std::unique_ptr<ShapeGraph> MapConverter::convertDataToAxial(Communicator *comm,
178175
int dataMapShapeRefCol = usermap->getAttributeTable().getOrInsertColumn("Data Map Ref");
179176

180177
AttributeTable& input = shapemap.getAttributeTable();
181-
auto keyIter = keys.begin();
182178
for (auto& line: lines) {
183179
if (copydata){
184-
auto& row = input.getRow(AttributeKey(keyIter->second));
180+
auto& row = input.getRow(AttributeKey(line.second.second));
185181
for (auto inOutColumn: inOutColumns){
186182
extraAttr[inOutColumn.second] = row.getValue(inOutColumn.first);
187183
}
188184
}
189-
extraAttr[dataMapShapeRefCol] = keyIter->second;
190-
usermap->makeLineShape(line.second, false, false, extraAttr);
191-
++keyIter;
185+
extraAttr[dataMapShapeRefCol] = line.second.second;
186+
usermap->makeLineShape(line.second.first, false, false, extraAttr);
192187
}
193188

194189
// n.b. make connections also initialises attributes
@@ -298,8 +293,9 @@ std::unique_ptr<ShapeGraph> MapConverter::convertDrawingToSegment(Communicator *
298293
comm->CommPostMessage( Communicator::CURRENT_STEP, 1 );
299294
}
300295

301-
std::map<int,Line> lines;
302-
std::map<int,int> layers; // this is used to say which layer it originated from
296+
// second number in internal pair is used to say which layer it originated from
297+
std::map<int, std::pair<Line, int>> lines;
298+
303299
bool recordlayer = false;
304300

305301
QtRegion region;
@@ -318,8 +314,7 @@ std::unique_ptr<ShapeGraph> MapConverter::convertDrawingToSegment(Communicator *
318314
}
319315
std::vector<SimpleLine> newLines = pixel.getAllShapesAsLines();
320316
for (const auto& line: newLines) {
321-
lines.insert(std::make_pair(count, Line(line.start(), line.end())));
322-
layers.insert(std::make_pair(count,j));
317+
lines.insert(std::make_pair(count, std::make_pair(Line(line.start(), line.end()), j)));
323318
count++;
324319
}
325320
pixel.setShow(false);
@@ -359,9 +354,9 @@ std::unique_ptr<ShapeGraph> MapConverter::convertDrawingToSegment(Communicator *
359354
for (auto & line: lines) {
360355
if (recordlayer)
361356
{
362-
layerAttributes[layerCol] = float(layers.find(line.first)->second);
357+
layerAttributes[layerCol] = float(line.second.second);
363358
}
364-
usermap->makeLineShape(line.second, false, false, layerAttributes);
359+
usermap->makeLineShape(line.second.first, false, false, layerAttributes);
365360
}
366361

367362
// make it!
@@ -380,8 +375,7 @@ std::unique_ptr<ShapeGraph> MapConverter::convertDataToSegment(Communicator *com
380375
comm->CommPostMessage( Communicator::CURRENT_STEP, 1 );
381376
}
382377

383-
std::map<int,Line> lines;
384-
std::map<int,int> keys;
378+
std::map<int, std::pair<Line, int>> lines;
385379

386380
// no longer requires m_region
387381
//m_region = shapemap.getRegion();
@@ -394,8 +388,7 @@ std::unique_ptr<ShapeGraph> MapConverter::convertDataToSegment(Communicator *com
394388
int key = shape.first;
395389
std::vector<Line> shapeLines = shape.second.getAsLines();
396390
for(Line line: shapeLines) {
397-
lines.insert(std::make_pair(count,line));
398-
keys.insert(std::make_pair(count,key));
391+
lines.insert(std::make_pair(count, std::make_pair(line, key)));
399392
count++;
400393
}
401394
}
@@ -464,17 +457,15 @@ std::unique_ptr<ShapeGraph> MapConverter::convertDataToSegment(Communicator *com
464457
int dataMapShapeRefCol = usermap->getAttributeTable().getOrInsertColumn("Data Map Ref");
465458

466459
AttributeTable& input = shapemap.getAttributeTable();
467-
auto keyIter = keys.begin();
468460
for (auto& line: lines) {
469461
if (copydata){
470-
auto& row = input.getRow(AttributeKey(keyIter->second));
462+
auto& row = input.getRow(AttributeKey(line.second.second));
471463
for (auto inOutColumn: inOutColumns){
472464
extraAttr[inOutColumn.second] = row.getValue(inOutColumn.first);
473465
}
474466
}
475-
extraAttr[dataMapShapeRefCol] = keyIter->second;
476-
usermap->makeLineShape(line.second, false, false, extraAttr);
477-
++keyIter;
467+
extraAttr[dataMapShapeRefCol] = line.second.second;
468+
usermap->makeLineShape(line.second.first, false, false, extraAttr);
478469
}
479470

480471
// start to be a little bit more efficient about memory now we are hitting the limits

salalib/mgraph.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ bool MetaGraph::analyseGraph( Communicator *communicator, Options options , bool
302302
if (m_view_class & VIEWVGA && !getDisplayedPointMap().isSelected()) {
303303
return false;
304304
}
305-
else if (m_view_class & VIEWAXIAL && !getDisplayedShapeGraph().isSelected()) {
305+
else if (m_view_class & VIEWAXIAL && !getDisplayedShapeGraph().hasSelectedElements()) {
306306
return false;
307307
}
308308
}
@@ -553,7 +553,7 @@ int MetaGraph::makeIsovistPath(Communicator *communicator, double fov, bool simp
553553
}
554554

555555
// must have a selection: the selected shapes will form the set from which to create the isovist paths
556-
if (!map->isSelected()) {
556+
if (!map->hasSelectedElements()) {
557557
return 0;
558558
}
559559

0 commit comments

Comments
 (0)