This repository has been archived on 2020-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
Files
Indoor/floorplan/v2/FloorplanLINT.h
2018-10-25 11:50:12 +02:00

279 lines
8.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* © Copyright 2014 Urheberrechtshinweis
* Alle Rechte vorbehalten / All Rights Reserved
*
* Programmcode ist urheberrechtlich geschuetzt.
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
* Keine Verwendung ohne explizite Genehmigung.
* (vgl. § 106 ff UrhG / § 97 UrhG)
*/
#ifndef FLOORPLANLINT_H
#define FLOORPLANLINT_H
#include "Floorplan.h"
#include "../../geo/BBox2.h"
#include <iostream>
#include <vector>
namespace Floorplan {
class LINT {
public:
/** possible issue types */
enum class Type {
WARN,
ERR,
};
/** type -> string */
static std::string getTypeStr(const Type t) {
switch(t) {
case Type::WARN: return "WARNING";
case Type::ERR: return "ERROR";
default: throw Exception("code error. invalid type. todo!");
}
}
/** a detected issue */
struct Issue {
Type type;
const Floor* floor;
std::string desc;
Issue(const Type type, const Floor* floor, const std::string& desc) : type(type), floor(floor), desc(desc) {;}
/** issue to string */
std::string asString() const {
return getTypeStr(type) + ": " + "floor '" + floor->name + "': " + desc;
}
};
using Issues = std::vector<Issue>;
public:
/** throw in case of errors within the map */
static void assertOK(IndoorMap* map) {
const Issues issues = check(map);
int err = 0;
for (const Issue& i : issues) {
std::cout << i.asString() << std::endl;
if (i.type == Type::ERR) {++err;}
}
if (err > 0) {
// throw Exception("detected floorplan errors");
}
}
/** get all errors within the map */
static Issues check(IndoorMap* map) {
Issues res;
for (const Floor* floor : map->floors) {
// outline present?
if (floor->outline.empty()) {
res.push_back(Issue(Type::ERR, floor, "has no outline"));
}
// check outline
for (const FloorOutlinePolygon* poly : floor->outline) {
checkOutline(res, floor, poly);
}
// check obstacles
for (const FloorObstacle* obs : floor->obstacles) {
checkObstacle(res, map, floor, obs);
}
// check fingerprints
for (const FingerprintLocation* fpl : floor->fpLocations) {
checkFingerprintLoc(res, floor, fpl);
}
// check stairs
for (const Stair* s : floor->stairs) {
checkStair(res, map, floor, s);
}
// check elevators
for (const Elevator* e : floor->elevators) {
checkElevator(res, map, floor, e);
}
}
// done
return res;
}
private:
/** check a floor's outline */
static void checkOutline(Issues& res, const Floor* floor, const FloorOutlinePolygon* poly) {
// number of points valid?
if (poly->poly.points.size() < 3) {res.push_back(Issue(Type::ERR, floor, "' outline '" + poly->name + "' needs at least 3 edges"));}
if (poly->poly.points.size() > 1024) {res.push_back(Issue(Type::ERR, floor, "' outline '" + poly->name + "' has too many edges"));}
// outline size [bbox] valid?
BBox2 outline;
for (const Point2 pt : poly->poly.points) { outline.add(pt); }
const Point2 size = outline.getSize();
if (size.x < 1.0 || size.y < 1.0) {res.push_back(Issue(Type::ERR, floor, "' outline '" + poly->name + "' seems too small"));}
}
/** check walls, doors, ... */
static void checkObstacle(Issues& res, const IndoorMap* map, const Floor* floor, const FloorObstacle* fo) {
// line? -> check
const FloorObstacleLine* line = dynamic_cast<const FloorObstacleLine*>(fo);
if (line) {
const float len_m = line->from.getDistance(line->to);
if (len_m < 0.15) {
res.push_back(Issue(Type::WARN, floor, "' line-obstacle is too short: " + std::to_string(len_m) + " meter from " + line->from.asString() + " to " + line->to.asString()));
}
}
// door? -> check
const FloorObstacleDoor* door = dynamic_cast<const FloorObstacleDoor*>(fo);
if (door) {
const float len_m = door->from.getDistance(door->to);
if (len_m < 0.40) {
res.push_back(Issue(Type::ERR, floor, "' door is too narrow: " + std::to_string(len_m) + " meter from " + door->from.asString() + " to " + door->to.asString()));
}
#pragma message ("TODO!")
// try {
// Ray3D::ModelFactory fac(map);
// fac.getDoorAbove(floor, door);
// } catch (Exception e) {
// res.push_back(Issue(Type::ERR, floor, std::string(e.what()) + "[from" + door->from.asString() + " to " + door->to.asString() + "]"));
// }
}
// pillar? -> check
const FloorObstacleCircle* circle = dynamic_cast<const FloorObstacleCircle*>(fo);
if (circle) {
const float len_m = circle->radius;
if (len_m < 0.20) {
res.push_back(Issue(Type::ERR, floor, "' pillar is too narrow: " + std::to_string(len_m) + " meter at " + circle->center.asString()));
}
}
}
static void checkFingerprintLoc(Issues& res, const Floor* floor, const FingerprintLocation* fpl) {
const std::string note = "does it belong to a stair? if so: fine! Note: fingerprints are currently measured using smartphones and smartphone are held by the pedestian. thus: fingerprints are ~1.3 meter above ground";
if (fpl->heightAboveFloor < 0.8) {
res.push_back(Issue(Type::ERR, floor, std::string() + "fingerprint " + fpl->name + " @ " + fpl->getPosition(*floor).asString() + " is too near to the floor. " + note));
} else if (fpl->heightAboveFloor > 2.0) {
res.push_back(Issue(Type::WARN, floor, std::string() + "fingerprint " + fpl->name + " @ " + fpl->getPosition(*floor).asString() + " is too high above the floor. " + note));
}
}
static void checkStair(Issues& res, const IndoorMap* map, const Floor* floor, const Stair* stair) {
// list of all heights where there is a floor;
std::vector<int> floorAtHeight_cm;
for (const Floor* f : map->floors) {
const int floorZ_cm = std::round(f->atHeight * 100);
floorAtHeight_cm.push_back(floorZ_cm); // integer height in cm
}
if (stair->getParts().empty()) {
res.push_back(Issue(Type::ERR, floor, "stair does not contain any parts! [empty stair]"));
return;
}
const std::vector<Floorplan::StairPart> parts = stair->getParts();
const std::vector<Floorplan::Quad3> quads = Floorplan::getQuads(parts, floor);
const Floorplan::Quad3& quadS = quads.front();
const Floorplan::Quad3& quadE = quads.back();
// start == end?
if (quadS.p1.z == quadE.p3.z) {
res.push_back(Issue(Type::ERR, floor, "stair start and end must not belong to the same floor!"));
}
// disconnected start? (MUST belong to the floor the stair is attached to)
if (quadS.p1.z != floor->getStartingZ()) {
res.push_back(Issue(Type::ERR, floor, "stair is not connected to the starting floor's ground! [open stair start]"));
}
// disconnected end? (must be long to ANY other floor within the map)
//if (quadE.p3.z != floor->getEndingZ()) {
const int stairEndingZ_cm = std::round( quadE.p3.z * 100 );
if(std::find(floorAtHeight_cm.begin(), floorAtHeight_cm.end(), stairEndingZ_cm) == floorAtHeight_cm.end()) {
res.push_back(Issue(Type::ERR, floor, "stair is not connected to the ending floor's ground! [open stair end]"));
}
for (int i = 0; i < (int) parts.size(); ++i) {
//const Floorplan::Quad3& quad = quads[i];
// disconnected within?
if (i > 0) {
if (quads[i-1].p4.z != quads[i-0].p1.z) {
res.push_back(Issue(Type::ERR, floor, "stair is disconnected within!"));
}
}
}
}
static void checkElevator(Issues& res, const IndoorMap* map, const Floor* floor, const Elevator* e) {
if (e->depth < 0.5) {
res.push_back(Issue(Type::ERR, floor, "elevator's @" + e->center.asString() + ": depth is too small: " + std::to_string(e->depth) + "m"));
}
if (e->width < 0.5) {
res.push_back(Issue(Type::ERR, floor, "elevator's @" + e->center.asString() + ": width is too small: " + std::to_string(e->width) + "m"));
}
if (e->height_m < 0.1) {
res.push_back(Issue(Type::ERR, floor, "elevator's @" + e->center.asString() + ": height is too small: " + std::to_string(e->height_m) + "m"));
}
// list of all heights where there is a floor;
std::vector<int> floorAtHeight_cm;
for (const Floor* f : map->floors) {
const int floorZ_cm = std::round(f->atHeight * 100);
floorAtHeight_cm.push_back(floorZ_cm); // integer height in cm
}
// disconnected end? (must be long to ANY other floor within the map)
const int elevEndZ_cm = std::round( (floor->getStartingZ() + e->height_m) * 100 );
if(std::find(floorAtHeight_cm.begin(), floorAtHeight_cm.end(), elevEndZ_cm) == floorAtHeight_cm.end()) {
res.push_back(Issue(Type::ERR, floor, "elevator @" + e->center.asString() + " is not connected to the ending floor's ground! [open elevator end]"));
}
}
};
}
#endif // FLOORPLANLINT_H