commit
0d2bb297b1
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "lib/osmparser"]
|
||||||
|
path = lib/osmparser
|
||||||
|
url = https://github.com/Lauchmelder23/OSMParser
|
|
@ -10,7 +10,7 @@ struct SDL_Renderer;
|
||||||
class Multipolygon
|
class Multipolygon
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Multipolygon(const std::shared_ptr<osmp::Relation>& relation, int width, int height, osmp::Bounds bounds);
|
Multipolygon(const osmp::Relation& relation, int width, int height, const osmp::Bounds& bounds);
|
||||||
|
|
||||||
void SetColor(int r, int g, int b);
|
void SetColor(int r, int g, int b);
|
||||||
void Draw(SDL_Renderer* renderer);
|
void Draw(SDL_Renderer* renderer);
|
||||||
|
|
1
lib/osmparser
Submodule
1
lib/osmparser
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 3d3abacee8242c08b350ca068dee82b95a102917
|
|
@ -1,26 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.10)
|
|
||||||
|
|
||||||
find_package(tinyxml2 CONFIG REQUIRED)
|
|
||||||
|
|
||||||
file(GLOB_RECURSE CPP_FILES
|
|
||||||
"src/*.cpp"
|
|
||||||
)
|
|
||||||
|
|
||||||
file(GLOB_RECURSE HPP_FILES
|
|
||||||
"include/*.hpp"
|
|
||||||
)
|
|
||||||
|
|
||||||
get_target_property(TINYXML2_INCLUDE_DIR tinyxml2::tinyxml2 INTERFACE_INCLUDE_DIRECTORIES)
|
|
||||||
|
|
||||||
add_library(osmp STATIC
|
|
||||||
${CPP_FILES}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(osmp PUBLIC
|
|
||||||
include
|
|
||||||
${TINYXML2_INCLUDE_DIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(osmp PRIVATE
|
|
||||||
tinyxml2::tinyxml2
|
|
||||||
)
|
|
|
@ -1,51 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include <util.hpp>
|
|
||||||
#include <osmtag.hpp>
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
class Object;
|
|
||||||
|
|
||||||
class IMember
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum class Type {
|
|
||||||
NODE, WAY, RELATION
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
IMember(const IMember& other) = delete;
|
|
||||||
virtual ~IMember() {}
|
|
||||||
|
|
||||||
IMember::Type GetType() const;
|
|
||||||
|
|
||||||
const std::vector<Tag>& GetTags() const;
|
|
||||||
size_t GetTagsSize() const;
|
|
||||||
const Tag& GetTag(size_t index) const;
|
|
||||||
std::string GetTag(const std::string& key) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
IMember(const tinyxml2::XMLElement* element, Object* parent, IMember::Type type);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
IMember::Type type;
|
|
||||||
Object* parent;
|
|
||||||
|
|
||||||
std::vector<Tag> tags;
|
|
||||||
// std::map<std::string, std::string> tags;
|
|
||||||
|
|
||||||
public:
|
|
||||||
uint64_t id;
|
|
||||||
std::string user;
|
|
||||||
unsigned int uid;
|
|
||||||
bool visible;
|
|
||||||
std::string version;
|
|
||||||
unsigned int changeset;
|
|
||||||
std::string timestamp;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "util.hpp"
|
|
||||||
#include <osmimember.hpp>
|
|
||||||
#include <osmtag.hpp>
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
class Object;
|
|
||||||
|
|
||||||
class Node : public IMember
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Node(const tinyxml2::XMLElement* xml, Object* parent);
|
|
||||||
|
|
||||||
public:
|
|
||||||
double lat, lon;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <util.hpp>
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
class Node;
|
|
||||||
class Way;
|
|
||||||
class Relation;
|
|
||||||
|
|
||||||
class Object
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Object(const std::string& file);
|
|
||||||
~Object();
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Node>> GetNodes() const;
|
|
||||||
size_t GetNodesSize() const;
|
|
||||||
std::shared_ptr<Node> GetNode(uint64_t id) const;
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Way>> GetWays() const;
|
|
||||||
size_t GetWaysSize() const;
|
|
||||||
std::shared_ptr<Way> GetWay(uint64_t id) const;
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Relation>> GetRelations() const;
|
|
||||||
size_t GetRelationsSize() const;
|
|
||||||
std::shared_ptr<Relation> GetRelation(uint64_t id) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
const std::string version;
|
|
||||||
const std::string generator;
|
|
||||||
|
|
||||||
Bounds bounds;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::map<uint64_t, std::shared_ptr<Node>> nodes;
|
|
||||||
std::map<uint64_t, std::shared_ptr<Way>> ways;
|
|
||||||
std::map<uint64_t, std::shared_ptr<Relation>> relations;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <osmobject.hpp>
|
|
||||||
#include <osmnode.hpp>
|
|
||||||
#include <osmway.hpp>
|
|
||||||
#include <osmtag.hpp>
|
|
||||||
#include <osmrelation.hpp>
|
|
||||||
#include <osmimember.hpp>
|
|
|
@ -1,43 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <util.hpp>
|
|
||||||
#include <osmtag.hpp>
|
|
||||||
#include <osmimember.hpp>
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
class Object;
|
|
||||||
|
|
||||||
class Relation : public IMember
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef struct sMember {
|
|
||||||
std::shared_ptr<IMember> member;
|
|
||||||
std::string role;
|
|
||||||
} Member;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Relation(const tinyxml2::XMLElement* xml, Object* parent);
|
|
||||||
|
|
||||||
std::string GetRelationType();
|
|
||||||
|
|
||||||
const std::vector<Member>& GetNodes() const;
|
|
||||||
size_t GetNodesSize() const;
|
|
||||||
const Member& GetNode(size_t index) const;
|
|
||||||
|
|
||||||
const std::vector<Member>& GetWays() const;
|
|
||||||
size_t GetWaysSize() const;
|
|
||||||
const Member& GetWay(size_t index) const;
|
|
||||||
|
|
||||||
bool HasNullMembers() const { return hasNullMembers; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string relationType;
|
|
||||||
bool hasNullMembers;
|
|
||||||
|
|
||||||
std::vector<Member> nodes;
|
|
||||||
std::vector<Member> ways;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
enum class TagKey {
|
|
||||||
NONE,
|
|
||||||
AERIALWAY, AEROWAY, AMENITY, BARRIER, BOUNDARY,
|
|
||||||
BUILDING, CRAFT, EMERGENCY, GEOLOGICAL, HEALTHCARE,
|
|
||||||
HIGHWAY, HISTORIC, LANDUSE, LEISURE, MANMADE, MILITARY,
|
|
||||||
NATURAL, OFFICE, PLACE, POWER, PUBLIC_TRANSPORT,
|
|
||||||
RAILWAY, ROUTE, SHOP, SPORT, TELECOM, TOURISM, WATER, WATERWAY
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
enum class TagValue {
|
|
||||||
NONE,
|
|
||||||
|
|
||||||
BUILDING_APARTMENTS, BUILDING_BUNGALOW, BUILDING_CABIN, BUILDING_DETACHED, BUILDING_DORMITORY, BUILDING_FARM, BUILDING_GER,
|
|
||||||
BUILDING_HOTEL, BUILDING_HOUSE, BUILDING_HOUSEBOAT, BUILDING_RESIDENTIAL, BUILDING_SEMIDETACHED_HOUSE, BUILDING_STATIC_CARAVAN,
|
|
||||||
BUILDING_TERRACE, BUILDING_COMMERCIAL, BUILDING_INDUSTRIAL, BUILDING_KIOSK, BUILDING_OFFICE, BUILDING_RETAIL, BUILDING_SUPERMARKET,
|
|
||||||
BUILDING_WAREHOUSE, BUILDING_CATHEDRAL, BUILDING_CHAPEL, BUILDING_CHURCH, BUILDING_MONASTERY, BUILDING_MOSQUE, BUILDING_PRESBYTERY,
|
|
||||||
BUILDING_RELIGIOUS, BUILDING_SHRINE, BUILDING_SYNAGOGUE, BUILDING_TEMPLE, BUILDING_BAKEHOUSE, BUILDING_CIVIC, BUILDING_FIRE_STATION,
|
|
||||||
BUILDING_GOVERNMENT, BUILDING_HOSPITAL, BUILDING_PUBLIC, BUILDING_TOILETS, BUILDING_TRAIN_STATION, BUILDING_TRANSPORTATION,
|
|
||||||
BUILDING_KINDERGARTEN, BUILDING_SCHOOL, BUILDING_UNIVERSITY
|
|
||||||
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef struct sTag
|
|
||||||
{
|
|
||||||
std::string k; // TODO: Should/could be an enum
|
|
||||||
std::string v;
|
|
||||||
} Tag;
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <util.hpp>
|
|
||||||
#include <osmtag.hpp>
|
|
||||||
#include <osmimember.hpp>
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
class Object;
|
|
||||||
class Node;
|
|
||||||
|
|
||||||
class Way : public IMember
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Way(const tinyxml2::XMLElement* way_elem, Object* parent);
|
|
||||||
|
|
||||||
const std::vector<std::shared_ptr<Node>>& GetNodes() const;
|
|
||||||
size_t GetNodesSize() const;
|
|
||||||
const std::shared_ptr<Node>& GetNode(size_t index) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool area, closed; // Closed := Startpoint = endpoint, Area := Closed AND certain conditions are not met
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::shared_ptr<Node>> nodes;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace tinyxml2
|
|
||||||
{
|
|
||||||
class XMLElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
typedef struct sBounds
|
|
||||||
{
|
|
||||||
double minlat, minlon, maxlat, maxlon;
|
|
||||||
} Bounds;
|
|
||||||
|
|
||||||
std::string GetSafeAttributeString(const tinyxml2::XMLElement* elem, const std::string& name);
|
|
||||||
double GetSafeAttributeFloat(const tinyxml2::XMLElement* elem, const std::string& name);
|
|
||||||
uint64_t GetSafeAttributeUint64(const tinyxml2::XMLElement* elem, const std::string& name);
|
|
||||||
bool GetSafeAttributeBool(const tinyxml2::XMLElement* elem, const std::string& name);
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
#include <osmimember.hpp>
|
|
||||||
|
|
||||||
#include <osmobject.hpp>
|
|
||||||
#include <tinyxml2.h>
|
|
||||||
|
|
||||||
namespace xml = tinyxml2;
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
IMember::IMember(const xml::XMLElement* element, Object* parent, IMember::Type type) :
|
|
||||||
type(type), parent(parent)
|
|
||||||
{
|
|
||||||
// Get Attribute
|
|
||||||
id = GetSafeAttributeUint64(element, "id");
|
|
||||||
user = GetSafeAttributeString(element, "user");
|
|
||||||
uid = GetSafeAttributeUint64(element, "uid");
|
|
||||||
visible = GetSafeAttributeBool(element, "visible");
|
|
||||||
version = GetSafeAttributeString(element, "version");
|
|
||||||
changeset = GetSafeAttributeUint64(element, "changeset");
|
|
||||||
timestamp = GetSafeAttributeString(element, "timestamp");
|
|
||||||
|
|
||||||
const xml::XMLElement* tag_element = element->FirstChildElement("tag");
|
|
||||||
while (tag_element != nullptr)
|
|
||||||
{
|
|
||||||
tags.push_back({
|
|
||||||
GetSafeAttributeString(tag_element, "k"),
|
|
||||||
GetSafeAttributeString(tag_element, "v"),
|
|
||||||
});
|
|
||||||
|
|
||||||
tag_element = tag_element->NextSiblingElement("tag");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IMember::Type IMember::GetType() const
|
|
||||||
{
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<Tag>& IMember::GetTags() const
|
|
||||||
{
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IMember::GetTagsSize() const
|
|
||||||
{
|
|
||||||
return tags.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
const Tag& IMember::GetTag(size_t index) const
|
|
||||||
{
|
|
||||||
return tags[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string IMember::GetTag(const std::string& key) const
|
|
||||||
{
|
|
||||||
for (const Tag& tag : tags)
|
|
||||||
{
|
|
||||||
if (tag.k == key)
|
|
||||||
{
|
|
||||||
return tag.v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
#include <osmnode.hpp>
|
|
||||||
|
|
||||||
#include <tinyxml2.h>
|
|
||||||
|
|
||||||
namespace xml = tinyxml2;
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
Node::Node(const tinyxml2::XMLElement* node_elem, Object* parent) :
|
|
||||||
IMember(node_elem, parent, IMember::Type::NODE)
|
|
||||||
{
|
|
||||||
// Get Attribute
|
|
||||||
lat = GetSafeAttributeFloat(node_elem, "lat");
|
|
||||||
lon = GetSafeAttributeFloat(node_elem, "lon");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,142 +0,0 @@
|
||||||
#include <osmobject.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <tinyxml2.h>
|
|
||||||
|
|
||||||
#include <osmnode.hpp>
|
|
||||||
#include <osmway.hpp>
|
|
||||||
#include <osmrelation.hpp>
|
|
||||||
|
|
||||||
namespace xml = tinyxml2;
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
Object::Object(const std::string& file) :
|
|
||||||
bounds({ 0.0f, 0.0f, 0.0f, 0.0f })
|
|
||||||
{
|
|
||||||
xml::XMLDocument doc;
|
|
||||||
xml::XMLError result = doc.LoadFile(file.c_str());
|
|
||||||
if (result != xml::XML_SUCCESS)
|
|
||||||
{
|
|
||||||
std::cerr << "Error: " << result << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
xml::XMLElement* root = doc.FirstChildElement();
|
|
||||||
|
|
||||||
// Get bounds
|
|
||||||
xml::XMLElement* bounds_elem = root->FirstChildElement("bounds");
|
|
||||||
bounds = {
|
|
||||||
GetSafeAttributeFloat(bounds_elem, "minlat"),
|
|
||||||
GetSafeAttributeFloat(bounds_elem, "minlon"),
|
|
||||||
GetSafeAttributeFloat(bounds_elem, "maxlat"),
|
|
||||||
GetSafeAttributeFloat(bounds_elem, "maxlon")
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get nodes
|
|
||||||
xml::XMLElement* node_elem = root->FirstChildElement("node");
|
|
||||||
while (node_elem != nullptr)
|
|
||||||
{
|
|
||||||
std::shared_ptr<Node> new_node = std::make_shared<Node>(node_elem, this);
|
|
||||||
nodes.insert(std::make_pair(new_node->id, new_node));
|
|
||||||
|
|
||||||
node_elem = node_elem->NextSiblingElement("node");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ways
|
|
||||||
xml::XMLElement* way_elem = root->FirstChildElement("way");
|
|
||||||
while (way_elem != nullptr)
|
|
||||||
{
|
|
||||||
std::shared_ptr<Way> new_way = std::make_shared<Way>(way_elem, this);
|
|
||||||
ways.insert(std::make_pair(new_way->id, new_way));
|
|
||||||
|
|
||||||
way_elem = way_elem->NextSiblingElement("way");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get relations
|
|
||||||
xml::XMLElement* relation_elem = root->FirstChildElement("relation");
|
|
||||||
while (relation_elem != nullptr)
|
|
||||||
{
|
|
||||||
std::shared_ptr<Relation> new_way = std::make_shared<Relation>(relation_elem, this);
|
|
||||||
relations.insert(std::make_pair(new_way->id, new_way));
|
|
||||||
|
|
||||||
relation_elem = relation_elem->NextSiblingElement("relation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object::~Object()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Node>> Object::GetNodes() const
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<Node>> vecNodes;
|
|
||||||
for (std::map<uint64_t, std::shared_ptr<Node>>::const_iterator it = nodes.begin(); it != nodes.end(); it++)
|
|
||||||
vecNodes.push_back(it->second);
|
|
||||||
|
|
||||||
return vecNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Object::GetNodesSize() const
|
|
||||||
{
|
|
||||||
return nodes.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> Object::GetNode(uint64_t id) const
|
|
||||||
{
|
|
||||||
std::map<uint64_t, std::shared_ptr<Node>>::const_iterator node = nodes.find(id);
|
|
||||||
if (node != nodes.end())
|
|
||||||
return node->second;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Way>> Object::GetWays() const
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<Way>> vecWays;
|
|
||||||
for (std::map<uint64_t, std::shared_ptr<Way>>::const_iterator it = ways.begin(); it != ways.end(); it++)
|
|
||||||
vecWays.push_back(it->second);
|
|
||||||
|
|
||||||
return vecWays;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Object::GetWaysSize() const
|
|
||||||
{
|
|
||||||
return ways.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Way> Object::GetWay(uint64_t id) const
|
|
||||||
{
|
|
||||||
std::map<uint64_t, std::shared_ptr<Way>>::const_iterator way = ways.find(id);
|
|
||||||
if (way != ways.end())
|
|
||||||
return way->second;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Relation>> Object::GetRelations() const
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<Relation>> vecRelations;
|
|
||||||
for (std::map<uint64_t, std::shared_ptr<Relation>>::const_iterator it = relations.begin(); it != relations.end(); it++)
|
|
||||||
vecRelations.push_back(it->second);
|
|
||||||
|
|
||||||
return vecRelations;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Object::GetRelationsSize() const
|
|
||||||
{
|
|
||||||
return relations.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Relation> Object::GetRelation(uint64_t id) const
|
|
||||||
{
|
|
||||||
std::map<uint64_t, std::shared_ptr<Relation>>::const_iterator relation = relations.find(id);
|
|
||||||
if (relation != relations.end())
|
|
||||||
return relation->second;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
#include <osmrelation.hpp>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <tinyxml2.h>
|
|
||||||
#include <osmobject.hpp>
|
|
||||||
#include <osmnode.hpp>
|
|
||||||
#include <osmway.hpp>
|
|
||||||
|
|
||||||
namespace xml = tinyxml2;
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
Relation::Relation(const xml::XMLElement* xml, Object* parent) :
|
|
||||||
IMember(xml, parent, IMember::Type::RELATION), hasNullMembers(false)
|
|
||||||
{
|
|
||||||
const xml::XMLElement* member_element = xml->FirstChildElement("member");
|
|
||||||
while (member_element != nullptr)
|
|
||||||
{
|
|
||||||
std::string memberType = GetSafeAttributeString(member_element, "type");
|
|
||||||
uint64_t ref = GetSafeAttributeUint64(member_element, "ref");
|
|
||||||
std::string role = GetSafeAttributeString(member_element, "role");
|
|
||||||
|
|
||||||
std::shared_ptr<IMember> member = nullptr;
|
|
||||||
if (memberType == "node") {
|
|
||||||
member = parent->GetNode(ref);
|
|
||||||
nodes.push_back({ member, role });
|
|
||||||
}
|
|
||||||
else if (memberType == "way") {
|
|
||||||
member = parent->GetWay(ref);
|
|
||||||
if (member == nullptr) {
|
|
||||||
hasNullMembers = true;
|
|
||||||
}
|
|
||||||
ways.push_back({ member, role });
|
|
||||||
}
|
|
||||||
|
|
||||||
member_element = member_element->NextSiblingElement("member");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Relation::GetRelationType()
|
|
||||||
{
|
|
||||||
return GetTag("type");
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<Relation::Member>& Relation::GetNodes() const
|
|
||||||
{
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Relation::GetNodesSize() const
|
|
||||||
{
|
|
||||||
return nodes.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
const Relation::Member& Relation::GetNode(size_t index) const
|
|
||||||
{
|
|
||||||
return nodes[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<Relation::Member>& Relation::GetWays() const
|
|
||||||
{
|
|
||||||
return ways;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Relation::GetWaysSize() const
|
|
||||||
{
|
|
||||||
return ways.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
const Relation::Member& Relation::GetWay(size_t index) const
|
|
||||||
{
|
|
||||||
return ways[index];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
#include <osmway.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <tinyxml2.h>
|
|
||||||
#include <osmobject.hpp>
|
|
||||||
#include <osmtag.hpp>
|
|
||||||
|
|
||||||
namespace xml = tinyxml2;
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
Way::Way(const tinyxml2::XMLElement* way_elem, Object* parent) :
|
|
||||||
IMember(way_elem, parent, IMember::Type::WAY)
|
|
||||||
{
|
|
||||||
area = GetSafeAttributeBool(way_elem, "area");
|
|
||||||
closed = false;
|
|
||||||
|
|
||||||
const xml::XMLElement* nd_elem = way_elem->FirstChildElement("nd");
|
|
||||||
while (nd_elem != nullptr)
|
|
||||||
{
|
|
||||||
nodes.push_back(
|
|
||||||
parent->GetNode(GetSafeAttributeUint64(nd_elem, "ref"))
|
|
||||||
);
|
|
||||||
|
|
||||||
nd_elem = nd_elem->NextSiblingElement("nd");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nodes.front() == nodes.back())
|
|
||||||
{
|
|
||||||
closed = true;
|
|
||||||
|
|
||||||
if (!area && GetTag("barrier") == "" && GetTag("highway") == "") // this code sucks, it can be done better
|
|
||||||
area = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<std::shared_ptr<Node>>& Way::GetNodes() const
|
|
||||||
{
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Way::GetNodesSize() const
|
|
||||||
{
|
|
||||||
return nodes.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::shared_ptr<Node>& Way::GetNode(size_t index) const
|
|
||||||
{
|
|
||||||
return nodes[index];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
#include <util.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <tinyxml2.h>
|
|
||||||
|
|
||||||
namespace xml = tinyxml2;
|
|
||||||
|
|
||||||
namespace osmp
|
|
||||||
{
|
|
||||||
#define FAILED(err) (err != xml::XML_SUCCESS)
|
|
||||||
|
|
||||||
std::string GetSafeAttributeString(const tinyxml2::XMLElement* elem, const std::string& name)
|
|
||||||
{
|
|
||||||
const char* buffer;
|
|
||||||
|
|
||||||
xml::XMLError result = elem->QueryStringAttribute(name.c_str(), &buffer);
|
|
||||||
if (FAILED(result))
|
|
||||||
return "";
|
|
||||||
|
|
||||||
std::string returnStr(buffer);
|
|
||||||
return returnStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
double GetSafeAttributeFloat(const tinyxml2::XMLElement* elem, const std::string& name)
|
|
||||||
{
|
|
||||||
double returnVal = 0.0f;
|
|
||||||
|
|
||||||
xml::XMLError result = elem->QueryDoubleAttribute(name.c_str(), &returnVal);
|
|
||||||
|
|
||||||
return returnVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t GetSafeAttributeUint64(const tinyxml2::XMLElement* elem, const std::string& name)
|
|
||||||
{
|
|
||||||
uint64_t returnVal = 0;
|
|
||||||
|
|
||||||
xml::XMLError result = elem->QueryUnsigned64Attribute(name.c_str(), &returnVal);
|
|
||||||
return returnVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetSafeAttributeBool(const tinyxml2::XMLElement* elem, const std::string& name)
|
|
||||||
{
|
|
||||||
bool returnVal = false;
|
|
||||||
|
|
||||||
xml::XMLError result = elem->QueryBoolAttribute(name.c_str(), &returnVal);
|
|
||||||
|
|
||||||
return returnVal;
|
|
||||||
}
|
|
||||||
}
|
|
22
src/main.cpp
22
src/main.cpp
|
@ -44,14 +44,14 @@ int main(int argc, char** argv)
|
||||||
int windowWidth = windowHeight * aspectRatio;
|
int windowWidth = windowHeight * aspectRatio;
|
||||||
|
|
||||||
// Fetch all the ways
|
// Fetch all the ways
|
||||||
std::vector<std::shared_ptr<osmp::Way>> ways = obj->GetWays();
|
osmp::Ways ways = obj->GetWays();
|
||||||
|
|
||||||
// Turn them into renderable ways by mapping the global coordinates to screen coordinates (do this smarter in the future pls)
|
// Turn them into renderable ways by mapping the global coordinates to screen coordinates (do this smarter in the future pls)
|
||||||
std::vector<Area> buildings;
|
std::vector<Area> buildings;
|
||||||
std::vector<Highway> highways;
|
std::vector<Highway> highways;
|
||||||
for (std::shared_ptr<osmp::Way> way : ways)
|
for (osmp::Way way : ways)
|
||||||
{
|
{
|
||||||
const std::vector<std::shared_ptr<osmp::Node>>& nodes = way->GetNodes();
|
const osmp::Nodes& nodes = way->GetNodes();
|
||||||
std::string highwayVal = way->GetTag("highway");
|
std::string highwayVal = way->GetTag("highway");
|
||||||
std::string railwayVal = way->GetTag("railway");
|
std::string railwayVal = way->GetTag("railway");
|
||||||
if (way->area)
|
if (way->area)
|
||||||
|
@ -119,9 +119,9 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch all relations
|
// Fetch all relations
|
||||||
std::vector<std::shared_ptr<osmp::Relation>> relations = obj->GetRelations();
|
osmp::Relations relations = obj->GetRelations();
|
||||||
std::vector<Multipolygon> multipolygons;
|
std::vector<Multipolygon> multipolygons;
|
||||||
for (const std::shared_ptr<osmp::Relation>& relation : relations)
|
for (const osmp::Relation& relation : relations)
|
||||||
{
|
{
|
||||||
if (relation->GetRelationType() == "multipolygon" && !relation->HasNullMembers())
|
if (relation->GetRelationType() == "multipolygon" && !relation->HasNullMembers())
|
||||||
{
|
{
|
||||||
|
@ -174,14 +174,14 @@ int main(int argc, char** argv)
|
||||||
SDL_SetRenderDrawColor(renderer, 240, 240, 250, 255);
|
SDL_SetRenderDrawColor(renderer, 240, 240, 250, 255);
|
||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
for (Multipolygon& multipolygon : multipolygons) {
|
for (Multipolygon& multipolygon : multipolygons) {
|
||||||
multipolygon.Draw(renderer);
|
multipolygon.Draw(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
//for (Area& area : buildings)
|
for (Area& area : buildings)
|
||||||
//{
|
{
|
||||||
// filledPolygonRGBA(renderer, area.x, area.y, area.length, area.r, area.g, area.b, 255);
|
filledPolygonRGBA(renderer, area.x, area.y, area.length, area.r, area.g, area.b, 255);
|
||||||
//}
|
}
|
||||||
|
|
||||||
for (Highway& highway : highways)
|
for (Highway& highway : highways)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "..\include\multipolygon.hpp"
|
#include "..\include\multipolygon.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -12,265 +13,167 @@
|
||||||
#include <SDL2_gfxPrimitives.h>
|
#include <SDL2_gfxPrimitives.h>
|
||||||
|
|
||||||
#define BREAKIF(x) if(relation->id == x) __debugbreak()
|
#define BREAKIF(x) if(relation->id == x) __debugbreak()
|
||||||
|
#define INDEXOF(x, y, n) (y * n + x)
|
||||||
|
|
||||||
struct TriangulationData {
|
struct TriangulationData {
|
||||||
std::vector<REAL> vertices, holes;
|
std::vector<REAL> vertices, holes;
|
||||||
std::vector<int> segments;
|
std::vector<int> segments;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Ring {
|
||||||
|
osmp::Nodes nodes;
|
||||||
|
bool inner;
|
||||||
|
int index;
|
||||||
|
bool hole = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RingGroup {
|
||||||
|
std::vector<Ring> rings;
|
||||||
|
};
|
||||||
|
|
||||||
// Map values from one interval [A, B] to another [a, b]
|
// Map values from one interval [A, B] to another [a, b]
|
||||||
inline double Map(double A, double B, double a, double b, double x)
|
inline double Map(double A, double B, double a, double b, double x)
|
||||||
{
|
{
|
||||||
return (x - A) * (b - a) / (B - A) + a;
|
return (x - A) * (b - a) / (B - A) + a;
|
||||||
}
|
}
|
||||||
|
|
||||||
Multipolygon::Multipolygon(const std::shared_ptr<osmp::Relation>& relation, int width, int height, osmp::Bounds bounds) :
|
// TODO: Implement better algorithm
|
||||||
|
[[nodiscard]] bool Intersect(double p1_x, double p1_y, double p2_x, double p2_y, double q1_x, double q1_y, double q2_x, double q2_y);
|
||||||
|
[[nodiscard]] bool Intersect(const osmp::Node& p1, const osmp::Node& p2, const osmp::Node& q1, const osmp::Node& q2);
|
||||||
|
[[nodiscard]] bool SelfIntersecting(const Ring& ring);
|
||||||
|
|
||||||
|
[[nodiscard]] bool BuildRing(Ring& ring, osmp::MemberWays& unassigned, int ringCount);
|
||||||
|
[[nodiscard]] bool AssignRings(std::vector<Ring>& rings, const osmp::MemberWays& members);
|
||||||
|
|
||||||
|
void FindAllContainedRings(const std::vector<bool>& containmentMatrix, int container, int numRings, std::vector<int>& buffer);
|
||||||
|
void FindAllContainedRingsThatArentContainedByUnusedRings(const std::vector<bool>& containmentMatrix, int container, int numRings, const std::vector<Ring>& unusedRings, std::vector<int>& buffer);
|
||||||
|
[[nodiscard]] int FindUncontainedRing(const std::vector<bool>& containmentMatrix, int rings, const std::vector<Ring>& unusedRings);
|
||||||
|
[[nodiscard]] bool PointInsideRing(const Ring& ring, const osmp::Node& point);
|
||||||
|
[[nodiscard]] bool IsRingContained(const Ring& r1, const Ring& r2);
|
||||||
|
[[nodiscard]] bool GroupRings(std::vector<RingGroup>& ringGroup, std::vector<Ring>& rings);
|
||||||
|
|
||||||
|
Multipolygon::Multipolygon(const osmp::Relation& relation, int width, int height, const osmp::Bounds& bounds) :
|
||||||
r(255), g(0), b(255), visible(true), rendering(RenderType::FILL), id(relation->id)
|
r(255), g(0), b(255), visible(true), rendering(RenderType::FILL), id(relation->id)
|
||||||
{
|
{
|
||||||
if (relation->HasNullMembers())
|
if (relation->HasNullMembers())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// BREAKIF(7344428);
|
const osmp::MemberWays& members = relation->GetWays();
|
||||||
// BREAKIF(6427823);
|
|
||||||
|
|
||||||
std::vector<TriangulationData> data;
|
/* Implement https://wiki.openstreetmap.org/wiki/Relation:multipolygon/Algorithm */
|
||||||
|
|
||||||
std::vector<osmp::Relation::Member> ways = relation->GetWays();
|
std::vector<Ring> rings;
|
||||||
std::vector<std::shared_ptr<osmp::Node>> nodes;
|
if (!AssignRings(rings, members))
|
||||||
int run = 1;
|
{
|
||||||
|
std::cerr << "Assigning rings has failed for multipolygon " << id << std::endl;
|
||||||
bool lastWasInner = false;
|
|
||||||
bool hasSeenOuter = false;
|
|
||||||
std::vector<std::vector<osmp::Relation::Member>> outerWays;
|
|
||||||
std::vector<std::vector<osmp::Relation::Member>> innerWays;
|
|
||||||
// Pre processing
|
|
||||||
for (osmp::Relation::Member member : ways) {
|
|
||||||
std::shared_ptr<osmp::Way> way = std::dynamic_pointer_cast<osmp::Way>(member.member);
|
|
||||||
if (member.role == "inner")
|
|
||||||
{
|
|
||||||
if (!hasSeenOuter) // TODO: Find better way to sort things
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (innerWays.empty() || !lastWasInner)
|
|
||||||
innerWays.push_back({});
|
|
||||||
|
|
||||||
innerWays.back().push_back(member);
|
|
||||||
lastWasInner = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hasSeenOuter = true;
|
|
||||||
if (outerWays.empty() || lastWasInner)
|
|
||||||
outerWays.push_back({});
|
|
||||||
|
|
||||||
outerWays.back().push_back(member);
|
|
||||||
lastWasInner = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outerWays.empty()) // There must always be an outer ring, anything else makes no sense
|
std::vector<RingGroup> ringGroups;
|
||||||
return;
|
GroupRings(ringGroups, rings);
|
||||||
|
|
||||||
auto jt = outerWays.begin();
|
char* triSwitches = "zpNBQ";
|
||||||
bool currentIsInner = false;
|
for (const RingGroup& ringGroup : ringGroups)
|
||||||
while (!outerWays.empty() || !innerWays.empty())
|
|
||||||
{
|
{
|
||||||
std::vector<osmp::Relation::Member> member = *jt;
|
TriangulationData td;
|
||||||
auto it = member.begin();
|
|
||||||
while (!member.empty())
|
bool valid = true;
|
||||||
|
for (const Ring& ring : ringGroup.rings)
|
||||||
{
|
{
|
||||||
if (it == member.end())
|
|
||||||
it = member.begin();
|
|
||||||
std::shared_ptr<osmp::Way> way = std::dynamic_pointer_cast<osmp::Way>(it->member);
|
|
||||||
|
|
||||||
// Several possible scenarios:
|
|
||||||
// Closed way
|
|
||||||
// Outer edge
|
|
||||||
// Append all nodes to the triangulation data
|
|
||||||
// Inner edge
|
|
||||||
// Append all nodes to the triangulation data
|
|
||||||
// Calculate average of nodes to get coordinates of the hole
|
|
||||||
//
|
|
||||||
// Open way
|
|
||||||
// Read next way until way is closed. This MUST happen, if the way remains open the OSM data is faulty and should be discarded
|
|
||||||
// Continue with Closed way algorithm
|
|
||||||
|
|
||||||
bool inner = (it->role == "inner");
|
|
||||||
std::vector<std::shared_ptr<osmp::Node>> wayNodes = way->GetNodes();
|
|
||||||
|
|
||||||
if (run == 1) {
|
|
||||||
nodes.insert(nodes.begin(), wayNodes.begin(), wayNodes.end());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (nodes.back() == wayNodes.front()) {
|
|
||||||
nodes.insert(nodes.end(), wayNodes.begin() + 1, wayNodes.end());
|
|
||||||
}
|
|
||||||
else if (nodes.back() == wayNodes.back()) {
|
|
||||||
nodes.insert(nodes.end(), wayNodes.rbegin() + 1, wayNodes.rend());
|
|
||||||
}
|
|
||||||
else if (nodes.front() == wayNodes.back()) {
|
|
||||||
nodes.insert(nodes.begin(), wayNodes.begin(), wayNodes.end() - 1);
|
|
||||||
}
|
|
||||||
else if (nodes.front() == wayNodes.front()) {
|
|
||||||
nodes.insert(nodes.begin(), wayNodes.rbegin(), wayNodes.rend() - 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
it++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
it = member.erase(it);
|
|
||||||
|
|
||||||
run++;
|
|
||||||
|
|
||||||
if (!(way->closed)) {
|
|
||||||
if (nodes.size() > 1 && nodes.front() == nodes.back())
|
|
||||||
{
|
|
||||||
// nodes.pop_back();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes.pop_back();
|
|
||||||
|
|
||||||
if (!inner || data.empty())
|
|
||||||
{
|
|
||||||
data.push_back({});
|
|
||||||
}
|
|
||||||
TriangulationData& td = data.back();
|
|
||||||
|
|
||||||
// Push all vertices to data
|
|
||||||
std::vector<REAL> vertices;
|
std::vector<REAL> vertices;
|
||||||
std::map<int, int> duplicates;
|
for (const osmp::Node& node : ring.nodes) {
|
||||||
int n = td.vertices.size() / 2;
|
|
||||||
for (const std::shared_ptr<osmp::Node>& node : nodes) {
|
|
||||||
double x = Map(bounds.minlon, bounds.maxlon, 0, width, node->lon);
|
double x = Map(bounds.minlon, bounds.maxlon, 0, width, node->lon);
|
||||||
double y = height - Map(bounds.minlat, bounds.maxlat, 0, height, node->lat);
|
double y = height - Map(bounds.minlat, bounds.maxlat, 0, height, node->lat);
|
||||||
|
|
||||||
auto xit = std::find(td.vertices.begin(), td.vertices.end(), x);
|
vertices.push_back(x);
|
||||||
auto yit = std::find(td.vertices.begin(), td.vertices.end(), y);
|
vertices.push_back(y);
|
||||||
if (std::distance(xit, yit) == 1) {
|
|
||||||
duplicates.insert(std::make_pair(n, std::distance(td.vertices.begin(), xit) / 2));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
vertices.push_back(x);
|
|
||||||
vertices.push_back(y);
|
|
||||||
}
|
|
||||||
n++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inner)
|
int segment = td.vertices.size() / 2;
|
||||||
{
|
for (int i = 0; i < vertices.size() / 2; i += 1) {
|
||||||
// Calculate data of hole by using the average position of all inner vertices (that should work right?, probably not...)
|
td.segments.push_back(segment + i);
|
||||||
REAL holeX = 0.0f;
|
td.segments.push_back(segment + i + 1);
|
||||||
REAL holeY = 0.0f;;
|
}
|
||||||
|
td.segments.back() = td.vertices.size() / 2;
|
||||||
|
|
||||||
|
td.vertices.insert(td.vertices.end(), vertices.begin(), vertices.end());
|
||||||
|
|
||||||
|
if (ring.hole) {
|
||||||
|
double holeX = 0.0f;
|
||||||
|
double holeY = 0.0f;
|
||||||
for (int i = 0; i < vertices.size(); i += 2)
|
for (int i = 0; i < vertices.size(); i += 2)
|
||||||
{
|
{
|
||||||
holeX += vertices[i];
|
holeX += vertices[i];
|
||||||
holeY += vertices[i + 1];
|
holeY += vertices[i + 1];
|
||||||
}
|
}
|
||||||
holeX /= (vertices.size() / 2);
|
|
||||||
holeY /= (vertices.size() / 2);
|
holeX /= vertices.size() / 2;
|
||||||
|
holeY /= vertices.size() / 2;
|
||||||
|
|
||||||
td.holes.push_back(holeX);
|
td.holes.push_back(holeX);
|
||||||
td.holes.push_back(holeY);
|
td.holes.push_back(holeY);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get segments
|
// TODO: Find better way to check for duplicates
|
||||||
int segNum = td.vertices.size() / 2;
|
for (int i = 0; i < td.vertices.size(); i += 2) {
|
||||||
for (int i = 0; i < vertices.size(); i += 2) {
|
for (int j = 0; j < td.vertices.size(); j += 2) {
|
||||||
auto dit = duplicates.find(segNum);
|
if (i == j) continue;
|
||||||
if (dit != duplicates.end())
|
|
||||||
{
|
|
||||||
td.segments.push_back(dit->second);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
td.segments.push_back(segNum++);
|
|
||||||
}
|
|
||||||
|
|
||||||
dit = duplicates.find(segNum);
|
if (td.vertices[i] == td.vertices[j] && td.vertices[i + 1] == td.vertices[j + 1])
|
||||||
if (dit != duplicates.end())
|
|
||||||
{
|
{
|
||||||
td.segments.push_back(dit->second);
|
valid = false;
|
||||||
}
|
break;
|
||||||
else
|
|
||||||
{
|
|
||||||
td.segments.push_back(segNum);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
td.segments.back() = td.vertices.size() / 2;
|
|
||||||
|
|
||||||
td.vertices.insert(td.vertices.end(), vertices.begin(), vertices.end());
|
|
||||||
nodes.clear();
|
|
||||||
run = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentIsInner) {
|
|
||||||
innerWays.erase(innerWays.begin());
|
|
||||||
jt = outerWays.begin();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
outerWays.erase(outerWays.begin());
|
|
||||||
jt = innerWays.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
currentIsInner = !currentIsInner;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* triswitches = "zpNBQ";
|
|
||||||
for (TriangulationData& td : data)
|
|
||||||
{
|
|
||||||
triangulateio in;
|
|
||||||
|
|
||||||
in.numberofpoints = td.vertices.size() / 2;
|
|
||||||
in.pointlist = td.vertices.data();
|
|
||||||
in.pointmarkerlist = NULL;
|
|
||||||
|
|
||||||
in.numberofpointattributes = 0;
|
if (valid)
|
||||||
in.numberofpointattributes = NULL;
|
{
|
||||||
|
triangulateio in;
|
||||||
|
|
||||||
in.numberofholes = td.holes.size() / 2;
|
in.numberofpoints = td.vertices.size() / 2;
|
||||||
in.holelist = td.holes.data();
|
in.pointlist = td.vertices.data();
|
||||||
|
in.pointmarkerlist = NULL;
|
||||||
|
|
||||||
in.numberofsegments = td.segments.size() / 2;
|
in.numberofpointattributes = 0;
|
||||||
in.segmentlist = td.segments.data();
|
in.numberofpointattributes = NULL;
|
||||||
in.segmentmarkerlist = NULL;
|
|
||||||
|
|
||||||
in.numberofregions = 0;
|
in.numberofholes = td.holes.size() / 2;
|
||||||
in.regionlist = NULL;
|
in.holelist = td.holes.data();
|
||||||
|
|
||||||
triangulateio out;
|
in.numberofsegments = td.segments.size() / 2;
|
||||||
out.pointlist = NULL;
|
in.segmentlist = td.segments.data();
|
||||||
out.pointmarkerlist = NULL;
|
in.segmentmarkerlist = NULL;
|
||||||
out.trianglelist = NULL;
|
|
||||||
out.segmentlist = NULL;
|
|
||||||
out.segmentmarkerlist = NULL;
|
|
||||||
|
|
||||||
triangulate(triswitches, &in, &out, NULL);
|
in.numberofregions = 0;
|
||||||
|
in.regionlist = NULL;
|
||||||
|
|
||||||
// TODO: memory leak go brrrr
|
triangulateio out;
|
||||||
polygons.push_back({});
|
out.pointlist = NULL;
|
||||||
for (int i = 0; i < in.numberofpoints * 2; i += 2) {
|
out.pointmarkerlist = NULL;
|
||||||
polygons.back().vertices.push_back({ in.pointlist[i], in.pointlist[i + 1] });
|
out.trianglelist = NULL;
|
||||||
// polygons.back().vertices.push_back(in.pointlist[i + 1]);
|
out.segmentlist = NULL;
|
||||||
|
out.segmentmarkerlist = NULL;
|
||||||
|
|
||||||
|
triangulate(triSwitches, &in, &out, NULL);
|
||||||
|
|
||||||
|
polygons.push_back({});
|
||||||
|
for (int i = 0; i < in.numberofpoints * 2; i += 2) {
|
||||||
|
polygons.back().vertices.push_back({ in.pointlist[i], in.pointlist[i + 1] });
|
||||||
|
// polygons.back().vertices.push_back(in.pointlist[i + 1]);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < out.numberoftriangles * 3; i++) {
|
||||||
|
polygons.back().indices.push_back(out.trianglelist[i]);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < in.numberofsegments * 2; i++) {
|
||||||
|
polygons.back().segments.push_back(in.segmentlist[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
trifree(out.trianglelist);
|
||||||
|
trifree(out.segmentlist);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < out.numberoftriangles * 3; i++) {
|
|
||||||
polygons.back().indices.push_back(out.trianglelist[i]);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < in.numberofsegments * 2; i++) {
|
|
||||||
polygons.back().segments.push_back(in.segmentlist[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
trifree(out.trianglelist);
|
|
||||||
trifree(out.segmentlist);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: Make a color map
|
// TODO: Make a color map
|
||||||
|
|
||||||
std::string tag = "";
|
std::string tag = "";
|
||||||
|
@ -659,8 +562,16 @@ void Multipolygon::Draw(SDL_Renderer* renderer)
|
||||||
if (!visible)
|
if (!visible)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// if (id != 6427823)
|
//for (const Polygon& polygon : polygons) {
|
||||||
// return;
|
// for (auto it = polygon.vertices.begin(); it != polygon.vertices.end() - 1; it++) {
|
||||||
|
// thickLineRGBA(renderer,
|
||||||
|
// it->x, it->y,
|
||||||
|
// (it+1)->x, (it+1)->y,
|
||||||
|
// 2,
|
||||||
|
// r, g, b, 255
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
for (const Polygon& polygon : polygons) {
|
for (const Polygon& polygon : polygons) {
|
||||||
switch(rendering)
|
switch(rendering)
|
||||||
|
@ -713,3 +624,306 @@ void Multipolygon::Draw(SDL_Renderer* renderer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Intersect(double p0_x, double p0_y, double p1_x, double p1_y, double p2_x, double p2_y, double p3_x, double p3_y)
|
||||||
|
{
|
||||||
|
if ((p0_x == p2_x && p0_y == p2_y) ||
|
||||||
|
(p0_x == p3_x && p0_y == p3_y) ||
|
||||||
|
(p1_x == p2_x && p1_y == p2_y) ||
|
||||||
|
(p1_x == p3_x && p1_y == p3_y))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float s1_x, s1_y, s2_x, s2_y;
|
||||||
|
s1_x = p1_x - p0_x; s1_y = p1_y - p0_y;
|
||||||
|
s2_x = p3_x - p2_x; s2_y = p3_y - p2_y;
|
||||||
|
|
||||||
|
float s, t;
|
||||||
|
s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
|
||||||
|
t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
|
||||||
|
|
||||||
|
if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
|
||||||
|
{
|
||||||
|
// Collision detected
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // No collision
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Intersect(const osmp::Node& p0, const osmp::Node& p1, const osmp::Node& p2, const osmp::Node& p3)
|
||||||
|
{
|
||||||
|
return Intersect(p0->lon, p0->lat, p1->lon, p1->lat, p2->lon, p2->lat, p3->lon, p3->lat);
|
||||||
|
}
|
||||||
|
bool SelfIntersecting(const Ring& ring)
|
||||||
|
{
|
||||||
|
struct Segment {
|
||||||
|
osmp::Node p1, p2;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get all segments
|
||||||
|
std::vector<Segment> segments;
|
||||||
|
for (auto it = ring.nodes.begin(); it != ring.nodes.end(); it++)
|
||||||
|
{
|
||||||
|
if (it == ring.nodes.end() - 1)
|
||||||
|
{
|
||||||
|
segments.push_back({
|
||||||
|
*it, ring.nodes.front()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
segments.push_back({
|
||||||
|
*it, *(it + 1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for self intersection (O(n^2)...)
|
||||||
|
for (auto it = segments.begin(); it != segments.end(); it++)
|
||||||
|
{
|
||||||
|
for (auto jt = segments.begin(); jt != segments.end(); jt++)
|
||||||
|
{
|
||||||
|
if (it == jt) continue;
|
||||||
|
|
||||||
|
if (Intersect(it->p1, it->p2, jt->p1, jt->p2))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuildRing(Ring& ring, osmp::MemberWays& unassigned, int ringCount)
|
||||||
|
{
|
||||||
|
const osmp::MemberWays original = unassigned;
|
||||||
|
|
||||||
|
// RA-2
|
||||||
|
int attempts = 0;
|
||||||
|
ring = Ring{ unassigned[attempts].way->GetNodes(), unassigned[attempts].role == "inner", ringCount };
|
||||||
|
unassigned.erase(unassigned.begin() + attempts);
|
||||||
|
|
||||||
|
RA3:
|
||||||
|
// RA-3
|
||||||
|
if (ring.nodes.front() == ring.nodes.back())
|
||||||
|
{
|
||||||
|
if (SelfIntersecting(ring))
|
||||||
|
{
|
||||||
|
unassigned = original;
|
||||||
|
attempts += 1;
|
||||||
|
if (unassigned.size() == attempts)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ring = Ring{ unassigned[attempts].way->GetNodes(), unassigned[attempts].role == "inner", ringCount };
|
||||||
|
goto RA3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ring.nodes.pop_back();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // RA-4
|
||||||
|
{
|
||||||
|
osmp::Node lastNode = ring.nodes.back();
|
||||||
|
for (auto it = unassigned.begin(); it != unassigned.end(); it++)
|
||||||
|
{
|
||||||
|
if (it->way->GetNodes().front() == lastNode)
|
||||||
|
{
|
||||||
|
ring.nodes.insert(ring.nodes.end(), it->way->GetNodes().begin() + 1, it->way->GetNodes().end());
|
||||||
|
unassigned.erase(it);
|
||||||
|
goto RA3;
|
||||||
|
}
|
||||||
|
else if (it->way->GetNodes().back() == lastNode)
|
||||||
|
{
|
||||||
|
ring.nodes.insert(ring.nodes.end(), it->way->GetNodes().rbegin() + 1, it->way->GetNodes().rend());
|
||||||
|
unassigned.erase(it);
|
||||||
|
goto RA3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No ring found
|
||||||
|
unassigned = original;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssignRings(std::vector<Ring>& rings, const osmp::MemberWays& members)
|
||||||
|
{
|
||||||
|
// Ring assignment
|
||||||
|
osmp::MemberWays unassigned = members;
|
||||||
|
int ringCount = 0;
|
||||||
|
while (!unassigned.empty())
|
||||||
|
{
|
||||||
|
rings.push_back({});
|
||||||
|
if (!BuildRing(rings.back(), unassigned, ringCount) || rings.size() > members.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ringCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cmp(const osmp::Node& a, const osmp::Node& b)
|
||||||
|
{
|
||||||
|
return (a->lon < b->lon);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindAllContainedRings(const std::vector<bool>& containmentMatrix, int container, int numRings, std::vector<int>& buffer)
|
||||||
|
{
|
||||||
|
buffer.clear();
|
||||||
|
for (int j = 0; j < numRings; j++) {
|
||||||
|
if (containmentMatrix[INDEXOF(container, j, numRings)])
|
||||||
|
buffer.push_back(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindAllContainedRingsThatArentContainedByUnusedRings(const std::vector<bool>& containmentMatrix, int container, int numRings, const std::vector<Ring>& unusedRings, std::vector<int>& buffer)
|
||||||
|
{
|
||||||
|
FindAllContainedRings(containmentMatrix, container, numRings, buffer);
|
||||||
|
|
||||||
|
for (auto j = buffer.begin(); j != buffer.end(); ) {
|
||||||
|
bool foundRing = false;
|
||||||
|
for (const Ring& ring : unusedRings) {
|
||||||
|
if (containmentMatrix[INDEXOF(ring.index, *j, numRings)]) {
|
||||||
|
foundRing = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(foundRing)
|
||||||
|
j = buffer.erase(j);
|
||||||
|
else
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Compare(const Ring& ring, int index) {
|
||||||
|
return (ring.index == index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FindUncontainedRing(const std::vector<bool>& containmentMatrix, int rings, const std::vector<Ring>& unusedRings)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < rings; j++) {
|
||||||
|
if (std::find_if(unusedRings.begin(), unusedRings.end(), [j](const Ring& ring) { return (ring.index == j); }) == unusedRings.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool isContained = false;
|
||||||
|
for (int i = 0; i < rings; i++) {
|
||||||
|
if (containmentMatrix[INDEXOF(i, j, rings)])
|
||||||
|
{
|
||||||
|
isContained = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isContained)
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PointInsideRing(const Ring& ring, const osmp::Node& point)
|
||||||
|
{
|
||||||
|
const osmp::Node& rightestNode = *(std::max_element(ring.nodes.begin(), ring.nodes.end(), cmp));
|
||||||
|
|
||||||
|
int intersections = 0;
|
||||||
|
for (auto it = ring.nodes.begin(); it != ring.nodes.end(); it++)
|
||||||
|
{
|
||||||
|
const osmp::Node& jt = ((it == ring.nodes.end() - 1) ? ring.nodes.front() : *(it + 1));
|
||||||
|
intersections += Intersect((*it)->GetLon(), (*it)->GetLat(),
|
||||||
|
jt->GetLon(), jt->GetLat(),
|
||||||
|
point->GetLon(), point->GetLat(),
|
||||||
|
rightestNode->GetLon() + 1.0f, point->GetLat()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (intersections % 2 != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRingContained(const Ring& r1, const Ring& r2)
|
||||||
|
{
|
||||||
|
// Test if any line segments are intersecting
|
||||||
|
// I don't think this is needed actually, the rings shouldn't overlap so testing if a node is inside is enough!
|
||||||
|
// Gonna leave this here tho in case we *do* need to see if a ring is completely contained
|
||||||
|
//for (auto it = r1.nodes.begin(); it != r1.nodes.end(); it++)
|
||||||
|
//{
|
||||||
|
// for (auto jt = r2.nodes.begin(); jt != r2.nodes.end(); jt++)
|
||||||
|
// {
|
||||||
|
// osmp::Node n1 = ((it == r1.nodes.end() - 1) ? r1.nodes.front() : *(it + 1));
|
||||||
|
// osmp::Node n2 = ((jt == r2.nodes.end() - 1) ? r2.nodes.front() : *(jt + 1));
|
||||||
|
|
||||||
|
// if (Intersect(*it, n1, *jt, n2))
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (PointInsideRing(r1, r2.nodes.front()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GroupRings(std::vector<RingGroup>& ringGroups, std::vector<Ring>& rings)
|
||||||
|
{
|
||||||
|
const std::vector<Ring> original = rings;
|
||||||
|
|
||||||
|
//RG-1
|
||||||
|
int ringNum = rings.size();
|
||||||
|
std::vector<bool> containmentMatrix(ringNum * ringNum);
|
||||||
|
|
||||||
|
for (int i = 0; i < ringNum; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < ringNum; j++)
|
||||||
|
{
|
||||||
|
if (i == j) {
|
||||||
|
containmentMatrix[INDEXOF(i, j, ringNum)] = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
containmentMatrix[INDEXOF(i, j, ringNum)] = IsRingContained(rings[i], rings[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RG-2 / RG-3
|
||||||
|
while (!rings.empty()) // TODO: Make this time out
|
||||||
|
{
|
||||||
|
int uncontainedRing = FindUncontainedRing(containmentMatrix, ringNum, rings);
|
||||||
|
if (uncontainedRing == -1) {
|
||||||
|
std::cerr << "Failed to find uncontained ring in step RG-2" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto it = std::find_if(rings.begin(), rings.end(), [uncontainedRing](const Ring& ring) { return (ring.index == uncontainedRing); });
|
||||||
|
if (it == rings.end())
|
||||||
|
{
|
||||||
|
std::cerr << "Uncontained Ring is out of range" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ringGroups.push_back({ {*it} });
|
||||||
|
rings.erase(it);
|
||||||
|
|
||||||
|
|
||||||
|
// RG-4
|
||||||
|
std::vector<int> containedRings;
|
||||||
|
FindAllContainedRingsThatArentContainedByUnusedRings(containmentMatrix, uncontainedRing, ringNum, rings, containedRings);
|
||||||
|
|
||||||
|
for (auto it = rings.begin(); it != rings.end(); ) {
|
||||||
|
if (std::find(containedRings.begin(), containedRings.end(), it->index) != containedRings.end()) {
|
||||||
|
ringGroups.back().rings.push_back(*it);
|
||||||
|
ringGroups.back().rings.back().hole = true;
|
||||||
|
it = rings.erase(it);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: RG-5 / RG-6 will be left out for now as they're optional.
|
||||||
|
// At least RG-6 should be implemented because not doing so might crash Triangle
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue