#ifndef FLOORPLANWRITER_H #define FLOORPLANWRITER_H #include #include "Floorplan.h" #include "../../Assertions.h" #include "../../lib/tinyxml/tinyxml2.h" namespace Floorplan { using XMLAttr = tinyxml2::XMLAttribute; using XMLElem = tinyxml2::XMLElement; using XMLDoc = tinyxml2::XMLDocument; /** * write floorplans as XML */ class Writer { public: static void writeToFile(const IndoorMap* map, const std::string& file) { XMLDoc doc; construct(doc, map); doc.SaveFile(file.c_str(), false); } static std::string writeToString(const IndoorMap* map) { XMLDoc doc; construct(doc, map); tinyxml2::XMLPrinter printer; doc.Accept( &printer ); return printer.CStr(); } private: static std::string toString(const ObstacleType t) { return std::to_string((int)t); } static std::string toString(const DoorType t) { return std::to_string((int)t); } static std::string toString(const Material m) { return std::to_string((int)m); } static std::string toString(const OutlineMethod m) { return std::to_string((int)m); } static void construct(XMLDoc& doc, const IndoorMap* map) { XMLElem* root = doc.NewElement("map"); doc.InsertEndChild(root); root->SetAttribute("width", map->width); root->SetAttribute("depth", map->depth); // add earth registration to the map addEarthReg(doc, root, map); // add all floors to the map addFloors(doc, root, map); } /** add earth registration to the map */ static void addEarthReg(XMLDoc& doc, XMLElem* root, const IndoorMap* map) { XMLElem* earthReg = doc.NewElement("earthReg"); { XMLElem* correspondences = doc.NewElement("correspondences"); for (const Floorplan::EarthPosMapPos* reg : map->earthReg.correspondences) { XMLElem* point = doc.NewElement("point"); point->SetAttribute("lat", reg->posOnEarth.lat); point->SetAttribute("lon", reg->posOnEarth.lon); point->SetAttribute("alt", reg->posOnEarth.height); point->SetAttribute("mx", reg->posOnMap_m.x); point->SetAttribute("my", reg->posOnMap_m.y); point->SetAttribute("mz", reg->posOnMap_m.z); correspondences->InsertEndChild(point); } earthReg->InsertEndChild(correspondences); } root->InsertEndChild(earthReg); } /** add all floors to the map */ static void addFloors(XMLDoc& doc, XMLElem* root, const IndoorMap* map) { XMLElem* floors = doc.NewElement("floors"); for(const Floor* mf : map->floors) {addFloor(doc, floors, mf);} root->InsertEndChild(floors); } /** add one floor to the floors-node */ static void addFloor(XMLDoc& doc, XMLElem* floors, const Floor* mf) { XMLElem* floor = doc.NewElement("floor"); floor->SetAttribute("atHeight", mf->atHeight); floor->SetAttribute("height", mf->height); floor->SetAttribute("name", mf->name.c_str()); floors->InsertEndChild(floor); addFloorOutline(doc, floor, mf); addFloorObstacles(doc, floor, mf); addFloorUnderlays(doc, floor, mf); // add all sorts of POI addFloorPOI(doc, floor, mf); addStairs(doc, floor, mf); addElevators(doc, floor, mf); } /** add all elevators to the floor */ static void addElevators(XMLDoc& doc, XMLElem* floor, const Floor* mf) { XMLElem* elevators = doc.NewElement("elevators"); for (const Elevator* elevator : mf->elevators) { XMLElem* elem = doc.NewElement("elevator"); elem->SetAttribute("cx", elevator->center.x); elem->SetAttribute("cy", elevator->center.y); elem->SetAttribute("width", elevator->width); elem->SetAttribute("depth", elevator->depth); elem->SetAttribute("height", elevator->height_m); elem->SetAttribute("rotation", elevator->rotation); elevators->InsertEndChild(elem); } floor->InsertEndChild(elevators); } /** add all stairs to the floor */ static void addStairs(XMLDoc& doc, XMLElem* floor, const Floor* mf) { XMLElem* stairs = doc.NewElement("stairs"); for (const Stair* _stair : mf->stairs) { XMLElem* elem = doc.NewElement("stair"); addMetaElement(doc, elem, _stair->getMeta()); if (dynamic_cast(_stair)) { const StairFreeform* stair = (StairFreeform*) _stair; elem->SetAttribute("type", 0); // TODO: other types? for (const StairPart& part : stair->parts) { XMLElem* elemPart = doc.NewElement("part"); elemPart->SetAttribute("connect", part.connectWithPrev); elemPart->SetAttribute("x1", part.start.x); elemPart->SetAttribute("y1", part.start.y); elemPart->SetAttribute("z1", part.start.z); elemPart->SetAttribute("x2", part.end.x); elemPart->SetAttribute("y2", part.end.y); elemPart->SetAttribute("z2", part.end.z); elemPart->SetAttribute("w", part.width); elem->InsertEndChild(elemPart); } } else { throw "not yet implemented"; } stairs->InsertEndChild(elem); } floor->InsertEndChild(stairs); } /** add all sorts of POI to the floor */ static void addFloorPOI(XMLDoc& doc, XMLElem* floor, const Floor* mf) { XMLElem* pois = doc.NewElement("pois"); for (const POI* poi : mf->pois) { XMLElem* elem = doc.NewElement("poi"); elem->SetAttribute("name", poi->name.c_str()); elem->SetAttribute("type", (int) poi->type); elem->SetAttribute("x", poi->pos.x); elem->SetAttribute("y", poi->pos.y); pois->InsertEndChild(elem); } floor->InsertEndChild(pois); XMLElem* gtpoints = doc.NewElement("gtpoints"); for (const GroundTruthPoint* gtp : mf->gtpoints) { XMLElem* elem = doc.NewElement("gtpoint"); elem->SetAttribute("id", gtp->id); elem->SetAttribute("x", gtp->pos.x); elem->SetAttribute("y", gtp->pos.y); elem->SetAttribute("z", gtp->pos.z); gtpoints->InsertEndChild(elem); } floor->InsertEndChild(gtpoints); XMLElem* accesspoints = doc.NewElement("accesspoints"); for (const AccessPoint* ap : mf->accesspoints) { XMLElem* accesspoint = doc.NewElement("accesspoint"); accesspoint->SetAttribute("name", ap->name.c_str()); accesspoint->SetAttribute("mac", ap->mac.c_str()); accesspoint->SetAttribute("x", ap->pos.x); accesspoint->SetAttribute("y", ap->pos.y); accesspoint->SetAttribute("z", ap->pos.z); accesspoint->SetAttribute("mdl_txp", ap->model.txp); accesspoint->SetAttribute("mdl_exp", ap->model.exp); accesspoint->SetAttribute("mdl_waf", ap->model.waf); addMetaElement(doc, accesspoint, ap->getMeta()); accesspoints->InsertEndChild(accesspoint); } floor->InsertEndChild(accesspoints); XMLElem* beacons = doc.NewElement("beacons"); for (const Beacon* b : mf->beacons) { XMLElem* beacon = doc.NewElement("beacon"); beacon->SetAttribute("name", b->name.c_str()); beacon->SetAttribute("mac", b->mac.c_str()); beacon->SetAttribute("major", b->major.c_str()); beacon->SetAttribute("minor", b->minor.c_str()); beacon->SetAttribute("uuid", b->uuid.c_str()); beacon->SetAttribute("x", b->pos.x); beacon->SetAttribute("y", b->pos.y); beacon->SetAttribute("z", b->pos.z); beacon->SetAttribute("mdl_txp", b->model.txp); beacon->SetAttribute("mdl_exp", b->model.exp); beacon->SetAttribute("mdl_waf", b->model.waf); beacons->InsertEndChild(beacon); } floor->InsertEndChild(beacons); XMLElem* fingerprints = doc.NewElement("fingerprints"); for (const FingerprintLocation* fpl : mf->fpLocations) { XMLElem* efpl = doc.NewElement("location"); efpl->SetAttribute("name", fpl->name.c_str()); efpl->SetAttribute("x", fpl->posOnFloor.x); efpl->SetAttribute("y", fpl->posOnFloor.y); efpl->SetAttribute("dz", fpl->heightAboveFloor); addMetaElement(doc, efpl, fpl->getMeta()); fingerprints->InsertEndChild(efpl); } floor->InsertEndChild(fingerprints); } static void addFloorOutline(XMLDoc& doc, XMLElem* floor, const Floor* mf) { XMLElem* outline = doc.NewElement("outline"); floor->InsertEndChild(outline); // XMLElem* add = doc.NewElement("add"); // outline->InsertEndChild(add); for (const FloorOutlinePolygon* poly : mf->outline) { addFloorOutlinePolygon(doc, outline, poly); } // XMLElem* remove = doc.NewElement("remove"); // outline->InsertEndChild(remove); // for (const FloorOutlinePolygon& poly : mf.outline.remove) { addFloorOutlinePolygon(doc, remove, poly); } } static void addFloorOutlinePolygon(XMLDoc& doc, XMLElem* dst, const FloorOutlinePolygon* poly) { std::string method = toString(poly->method); XMLElem* polygon = doc.NewElement("polygon"); polygon->SetAttribute("name", poly->name.c_str()); polygon->SetAttribute("method", method.c_str()); polygon->SetAttribute("outdoor", poly->outdoor); dst->InsertEndChild(polygon); for (Point2 p : poly->poly.points) { XMLElem* point = doc.NewElement("point"); point->SetAttribute("x", p.x); point->SetAttribute("y", p.y); // skip the z-attribute as the value is also given by the polygons floor: floor.atHeight polygon->InsertEndChild(point); } } /** write one element */ static void addMetaElement(XMLDoc& doc, XMLElem* other, const Meta* meta) { if (meta == nullptr) {return;} // nothing to add XMLElem* elem = doc.NewElement("meta"); // for (auto it : meta->params) { // elem->Attribute(it.first.c_str(), it.second.c_str()); // } for (const Meta::KeyVal& kv : meta->params) { // abc XMLElem* subElem = doc.NewElement("entry"); subElem->SetAttribute("key", kv.key.c_str()); subElem->SetText(kv.val.c_str()); elem->InsertEndChild(subElem); } other->InsertEndChild(elem); } /** write the tag */ static void addFloorUnderlays(XMLDoc& doc, XMLElem* floor, const Floor* mf) { XMLElem* other = doc.NewElement("underlays"); for (const UnderlayImage* kv : mf->underlays) { addFloorUnderlay(doc, other, kv); } floor->InsertEndChild(other); } /** write one underlay */ static void addFloorUnderlay(XMLDoc& doc, XMLElem* underlays, const UnderlayImage* img) { XMLElem* underlay = doc.NewElement("underlay"); underlay->SetAttribute("x", img->anchor.x); underlay->SetAttribute("y", img->anchor.y); underlay->SetAttribute("sx", img->scaleX); underlay->SetAttribute("sy", img->scaleY); underlay->SetAttribute("name", img->name.c_str()); underlay->SetAttribute("file", img->filename.c_str()); underlays->InsertEndChild(underlay); } static void addFloorObstacles(XMLDoc& doc, XMLElem* floor, const Floor* mf) { XMLElem* obstacles = doc.NewElement("obstacles"); for (FloorObstacle* fo : mf->obstacles) { if (dynamic_cast(fo)) { addFloorObstacleLine(doc, obstacles, (FloorObstacleLine*)fo); } else if (dynamic_cast(fo)) { addFloorObstacleWall(doc, obstacles, (FloorObstacleWall*)fo); } else if (dynamic_cast(fo)) { addFloorObstacleCircle(doc, obstacles, (FloorObstacleCircle*)fo); } else if (dynamic_cast(fo)) { addFloorObstacleDoor(doc, obstacles, (FloorObstacleDoor*)fo); } else if (dynamic_cast(fo)) { addFloorObstacleObject(doc, obstacles, (FloorObstacleObject*)fo); } } floor->InsertEndChild(obstacles); } /** write a wall obstacle (wall) */ static void addFloorObstacleWall(XMLDoc& doc, XMLElem* obstacles, FloorObstacleWall* wall) { XMLElem* oWall = doc.NewElement("wall"); obstacles->InsertEndChild(oWall); oWall->SetAttribute("material", toString(wall->material).c_str()); oWall->SetAttribute("type", toString(wall->type).c_str()); oWall->SetAttribute("x1", wall->from.x); oWall->SetAttribute("y1", wall->from.y); oWall->SetAttribute("x2", wall->to.x); oWall->SetAttribute("y2", wall->to.y); oWall->SetAttribute("thickness", wall->thickness_m); if (wall->height_m != 0) {oWall->SetAttribute("height", wall->height_m);} // doors? for (const FloorObstacleWallDoor* door : wall->doors) { XMLElem* oDoor = doc.NewElement("door"); oWall->InsertEndChild(oDoor); oDoor->SetAttribute("type", toString(door->type).c_str()); oDoor->SetAttribute("material", toString(door->material).c_str()); oDoor->SetAttribute("at", door->atLinePos); oDoor->SetAttribute("width", door->width); oDoor->SetAttribute("height", door->height); oDoor->SetAttribute("io", door->inOut); oDoor->SetAttribute("lr", door->leftRight); } } /** write a line obstacle (old walls, handrail, ..) */ static void addFloorObstacleLine(XMLDoc& doc, XMLElem* obstacles, FloorObstacleLine* line) { XMLElem* obstacle = doc.NewElement("line"); obstacle->SetAttribute("material", toString(line->material).c_str()); obstacle->SetAttribute("type", toString(line->type).c_str()); obstacle->SetAttribute("x1", line->from.x); obstacle->SetAttribute("y1", line->from.y); obstacle->SetAttribute("x2", line->to.x); obstacle->SetAttribute("y2", line->to.y); obstacle->SetAttribute("thickness", line->thickness_m); if (line->height_m != 0) {obstacle->SetAttribute("height", line->height_m);} obstacles->InsertEndChild(obstacle); } /** write a circle obstacle (pillar, ..) */ static void addFloorObstacleCircle(XMLDoc& doc, XMLElem* obstacles, FloorObstacleCircle* circle) { XMLElem* obstacle = doc.NewElement("circle"); obstacle->SetAttribute("material", toString(circle->material).c_str()); obstacle->SetAttribute("cx", circle->center.x); obstacle->SetAttribute("cy", circle->center.y); obstacle->SetAttribute("radius", circle->radius); if (circle->height != 0) {obstacle->SetAttribute("height", circle->height);} obstacles->InsertEndChild(obstacle); } /** write a door obstacle */ static void addFloorObstacleDoor(XMLDoc& doc, XMLElem* obstacles, FloorObstacleDoor* door) { XMLElem* obstacle = doc.NewElement("door"); obstacle->SetAttribute("material", toString(door->material).c_str()); obstacle->SetAttribute("type", toString(door->type).c_str()); obstacle->SetAttribute("x1", door->from.x); obstacle->SetAttribute("y1", door->from.y); obstacle->SetAttribute("x2", door->to.x); obstacle->SetAttribute("y2", door->to.y); obstacle->SetAttribute("height", door->height); obstacle->SetAttribute("swap", door->swap); obstacles->InsertEndChild(obstacle); } /** write an object-obstacle */ static void addFloorObstacleObject(XMLDoc& doc, XMLElem* obstacles, FloorObstacleObject* obj) { XMLElem* obstacle = doc.NewElement("object"); obstacle->SetAttribute("file", obj->file.c_str()); obstacle->SetAttribute("x", obj->pos.x); obstacle->SetAttribute("y", obj->pos.y); obstacle->SetAttribute("z", obj->pos.z); if (obj->rot.x != 0) {obstacle->SetAttribute("rx", obj->rot.x);} if (obj->rot.y != 0) {obstacle->SetAttribute("ry", obj->rot.y);} if (obj->rot.z != 0) {obstacle->SetAttribute("rz", obj->rot.z);} if (obj->scale.x != 1) {obstacle->SetAttribute("sx", obj->scale.x);} if (obj->scale.y != 1) {obstacle->SetAttribute("sy", obj->scale.y);} if (obj->scale.z != 1) {obstacle->SetAttribute("sz", obj->scale.z);} obstacles->InsertEndChild(obstacle); } }; } #endif // FLOORPLANWRITER_H