Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a47a4dd
Added option to avoid blank nodes by replacing them with a unique ide…
nicolano Apr 22, 2025
3ef86ff
Fixed and added some test cases
nicolano Apr 22, 2025
8535d5e
Position of the element in the way/relation is added to the id to dis…
nicolano Apr 24, 2025
1c1f0d1
Fixed new tests
nicolano Apr 24, 2025
957fddf
* Added metadata triple about osm2rdf version, dump time and used com…
nicolano Jun 17, 2025
6cf7681
Merge remote-tracking branch 'origin/add_option_to_avoid_blank_nodes'…
nicolano Jun 17, 2025
13b4fa3
* Add option triple for blank nodes option
nicolano Jun 17, 2025
a8db373
* Fixed wrong constants
nicolano Jun 17, 2025
cb5f66d
* Use writeSecondsAsISO method
nicolano Jun 17, 2025
0c3d6d1
Merge remote-tracking branch 'origin/add_metadata_triples' into olu_c…
nicolano Jun 17, 2025
d4e8949
* Fixed formatting of xsd datatypes
nicolano Jun 18, 2025
d927f00
Merge remote-tracking branch 'origin/add_metadata_triples' into olu_c…
nicolano Jun 18, 2025
7a90855
* Fixed formatting of xsd datatypes
nicolano Jun 18, 2025
2daefe3
Merge remote-tracking branch 'origin/add_metadata_triples' into olu_c…
nicolano Jun 18, 2025
77bd1e1
* Add inline keyword to global variable GIT_INFO to avoid "multiple d…
nicolano Jun 18, 2025
0c68fc1
Merge remote-tracking branch 'origin/add_metadata_triples' into olu_c…
nicolano Jun 18, 2025
61d1303
* As some config parameters have the opposite meaning than the option…
nicolano Jun 18, 2025
4f8cb8c
Merge remote-tracking branch 'origin/add_metadata_triples' into olu_c…
nicolano Jun 18, 2025
3bbd6db
Merge remote-tracking branch 'origin/master' into add_metadata_triples
nicolano Jul 10, 2025
18fd3c8
* Added new options from commit https://github.com/ad-freiburg/osm2rd…
nicolano Jul 10, 2025
5e043a0
Merge branch 'add_metadata_triples' into olu_compatibility
nicolano Jul 10, 2025
0650cc4
Merge branch 'master' into olu_compatibility
nicolano Sep 2, 2025
be5f53f
Added metadata triple that stores the IRI prefix for untagged nodes t…
nicolano Oct 10, 2025
e62cd48
Merge branch 'master' into olu_compatibility
nicolano Oct 10, 2025
d12f5d0
Merge with master
nicolano Oct 10, 2025
0b4612f
Fix literal
nicolano Oct 10, 2025
afb4b81
Merge branch 'added_missing_metadata_triple' into olu_compatibility
nicolano Oct 10, 2025
01cb6e3
updated submodules
nicolano Oct 11, 2025
0c7f051
Use same submodule tags as master
nicolano Oct 15, 2025
4e0056b
Merge remote-tracking branch 'origin/master' into add_option_to_avoid…
nicolano Oct 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/osm2rdf/config/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ struct Config {
std::string iriPrefixForUntaggedNodes =
osm2rdf::ttl::constants::IRI_PREFIX__OSM_NODE_UNTAGGED;

bool noBlankNodes = false;

int numThreads = std::thread::hardware_concurrency();

// Default settings for data
Expand Down
7 changes: 7 additions & 0 deletions include/osm2rdf/config/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,13 @@ const static inline std::string WKT_PRECISION_OPTION_LONG = "wkt-precision";
const static inline std::string WKT_PRECISION_OPTION_HELP =
"Precision (number of decimal digits) for WKT coordinates";

const static inline std::string BLANK_NODES_INFO =
"Blank nodes are masked";
const static inline std::string BLANK_NODES_OPTION_SHORT = "";
const static inline std::string BLANK_NODES_OPTION_LONG = "no-blank-nodes";
const static inline std::string BLANK_NODES_OPTION_HELP =
"Avoid blank nodes by using a unique identifier for each member";

const static inline std::string WRITE_RDF_STATISTICS_INFO =
"Storing RDF statistics as .stats.json";
const static inline std::string WRITE_RDF_STATISTICS_OPTION_SHORT = "";
Expand Down
1 change: 1 addition & 0 deletions include/osm2rdf/ttl/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const static inline std::string NAMESPACE__OSM2RDF_TAG = "osm2rdfkey";
const static inline std::string NAMESPACE__RDF = "rdf";
const static inline std::string NAMESPACE__WIKIDATA_ENTITY = "wd";
const static inline std::string NAMESPACE__XML_SCHEMA = "xsd";
const static inline std::string NAMESPACE__GENID = "genid";

// IRI Prefixes
const static inline std::string IRI_PREFIX__OSM_NODE_TAGGED = "https://www.openstreetmap.org/node/";
Expand Down
20 changes: 20 additions & 0 deletions include/osm2rdf/ttl/Writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,26 @@ class Writer {
// generateBlankNode creates a new unique identifier for a blank node.
std::string generateBlankNode();

// generateSkolem creates a unique identifier for an osm object member.
std::string generateSkolem(const std::string& id);

// generateSkolemForRelationMember creates a unique identifier for a member of
// a relation by combining the id of the relation and member with a letter
// indicating the object type ('r', 'w', or 'n'). The relative position of the
// object in the relation is appended behind 'p'. Example: "r1234w5678p3"
std::string generateSkolemForRelationMember(const uint64_t& relationId,
const uint64_t& memberId,
const std::string& memberType,
const size_t& relPos);

// generateSkolemForWayMember creates a unique identifier for a member of
// a way by combining the id of the way and member with a letter
// indicating the object type ('r', 'w', or 'n'). The relative position of the node in the way is
// appended behind 'p'. Example: "w1234n5678p0"
std::string generateSkolemForWayMember(const uint64_t& wayId,
const uint64_t& nodeId,
const size_t& relPos);

// Creates a IRI from given prefix p and string value v.
// Assumes that both p and v are "safe", that is, they can be used
// directly in the TTL
Expand Down
14 changes: 14 additions & 0 deletions src/config/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ std::string osm2rdf::config::Config::getInfo(std::string_view prefix) const {
oss << "\n" << prefix << osm2rdf::config::constants::SECTION_MISCELLANEOUS;
oss << "\n" << prefix << "Num Threads: " << numThreads;

if (noBlankNodes) {
oss << "\n"
<< prefix
<< osm2rdf::config::constants::BLANK_NODES_INFO;
}

if (!storeLocations.empty()) {
oss << "\n"
<< prefix << osm2rdf::config::constants::STORE_LOCATIONS_INFO << " "
Expand Down Expand Up @@ -408,6 +414,12 @@ void osm2rdf::config::Config::fromArgs(int argc, char** argv) {
osm2rdf::config::constants::WKT_PRECISION_OPTION_LONG,
osm2rdf::config::constants::WKT_PRECISION_OPTION_HELP, wktPrecision);

auto noBlankNodesOp =
parser.add<popl::Switch, popl::Attribute::advanced>(
osm2rdf::config::constants::BLANK_NODES_OPTION_SHORT,
osm2rdf::config::constants::BLANK_NODES_OPTION_LONG,
osm2rdf::config::constants::BLANK_NODES_OPTION_HELP);

auto writeRDFStatisticsOp =
parser.add<popl::Switch, popl::Attribute::advanced>(
osm2rdf::config::constants::WRITE_RDF_STATISTICS_OPTION_SHORT,
Expand Down Expand Up @@ -535,6 +547,8 @@ void osm2rdf::config::Config::fromArgs(int argc, char** argv) {

addSpatialRelsForUntaggedNodes = untaggedNodesSpatialRelsOp->is_set();

noBlankNodes = noBlankNodesOp->is_set();

addUntaggedNodes = !noUntaggedNodesOp->is_set();
if (iriPrefixForUntaggedNodesOp->is_set() &&
iriPrefixForUntaggedNodesOp->value().size() > 0) {
Expand Down
38 changes: 24 additions & 14 deletions src/osm/FactHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,16 +277,22 @@ void osm2rdf::osm::FactHandler<W>::relation(

std::string role = member.role();
if (role.empty()) role = "member";
const std::string& blankNode = _writer->generateBlankNode();

const std::string memberIRI = _config.noBlankNodes
? _writer->generateSkolemForRelationMember(
relation.id(), member.positive_ref(),
type, inRelPos)
: _writer->generateBlankNode();

_writer->writeTriple(
subj, _writer->generateIRIUnsafe(NAMESPACE__OSM_RELATION, "member"),
blankNode);
memberIRI);

_writer->writeTriple(blankNode, IRI__OSMREL__MEMBER_ID,
_writer->writeTriple(memberIRI, IRI__OSMREL__MEMBER_ID,
_writer->generateIRI(type, member.positive_ref()));
_writer->writeTriple(blankNode, IRI__OSMREL__MEMBER_ROLE,
_writer->writeTriple(memberIRI, IRI__OSMREL__MEMBER_ROLE,
_writer->generateLiteral(role));
_writer->writeLiteralTripleUnsafe(blankNode, IRI__OSMREL__MEMBER_POS,
_writer->writeLiteralTripleUnsafe(memberIRI, IRI__OSMREL__MEMBER_POS,
std::to_string(inRelPos++),
_iriXSDInteger);
}
Expand Down Expand Up @@ -348,11 +354,15 @@ void osm2rdf::osm::FactHandler<W>::way(const osm2rdf::osm::Way& way) {

if (_config.addMemberTriples && way.nodes().size()) {
size_t wayOrder = 0;
std::string lastBlankNode;
std::string lastMemberIRI;
auto lastNode = way.nodes().front();
for (const auto& node : way.nodes()) {
const std::string& blankNode = _writer->generateBlankNode();
_writer->writeTriple(subj, IRI__OSMWAY__NODE, blankNode);
const std::string memberIRI = _config.noBlankNodes
? _writer->generateSkolemForWayMember(
way.id(), node.positive_ref(), wayOrder)
: _writer->generateBlankNode();

_writer->writeTriple(subj, IRI__OSMWAY__NODE, memberIRI);

std::string nodeNamespace;
if (_config.iriPrefixForUntaggedNodes ==
Expand All @@ -365,17 +375,17 @@ void osm2rdf::osm::FactHandler<W>::way(const osm2rdf::osm::Way& way) {
}

_writer->writeTriple(
blankNode, osm2rdf::ttl::constants::IRI__OSMWAY__MEMBER_ID,
memberIRI, osm2rdf::ttl::constants::IRI__OSMWAY__MEMBER_ID,
_writer->generateIRI(nodeNamespace, node.positive_ref()));

_writer->writeLiteralTripleUnsafe(
blankNode, osm2rdf::ttl::constants::IRI__OSMWAY__MEMBER_POS,
memberIRI, osm2rdf::ttl::constants::IRI__OSMWAY__MEMBER_POS,
std::to_string(wayOrder++), _iriXSDInteger);

if (_config.addWayNodeSpatialMetadata && !lastBlankNode.empty() &&
if (_config.addWayNodeSpatialMetadata && !lastMemberIRI.empty() &&
node.location().valid() && lastNode.location().valid()) {
_writer->writeTriple(
lastBlankNode, IRI__OSMWAY__NEXT_NODE,
lastMemberIRI, IRI__OSMWAY__NEXT_NODE,
_writer->generateIRI(nodeNamespace, node.positive_ref()));
// Haversine distance
const double distanceLat = (node.location().lat_without_check() -
Expand All @@ -393,10 +403,10 @@ void osm2rdf::osm::FactHandler<W>::way(const osm2rdf::osm::Way& way) {
osm2rdf::osm::constants::METERS_IN_KM * 2 *
asin(sqrt(haversine));
_writer->writeLiteralTripleUnsafe(
lastBlankNode, IRI__OSMWAY__NEXT_NODE_DISTANCE,
lastMemberIRI, IRI__OSMWAY__NEXT_NODE_DISTANCE,
std::to_string(distance), "^^" + IRI__XSD__DECIMAL);
}
lastBlankNode = blankNode;
lastMemberIRI = memberIRI;
lastNode = node;
}
}
Expand Down
38 changes: 37 additions & 1 deletion src/ttl/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ osm2rdf::ttl::Writer<T>::Writer(const osm2rdf::config::Config& config,
"https://osm2rdf.cs.uni-freiburg.de/rdf/key#"},
{osm2rdf::ttl::constants::NAMESPACE__OSM2RDF_META,
"https://osm2rdf.cs.uni-freiburg.de/rdf/meta#"},
{osm2rdf::ttl::constants::NAMESPACE__GENID,
"http://osm2rdf.cs.uni-freiburg.de/.well-known/genid/"},
// https://wiki.openstreetmap.org/wiki/Sophox#How_OSM_data_is_stored
// https://github.com/Sophox/sophox/blob/master/osm2rdf/osmutils.py#L35-L39
// osm prefixes
Expand Down Expand Up @@ -388,7 +390,8 @@ void osm2rdf::ttl::Writer<T>::writeMetadata() {
writeOptionTriple(
osm2rdf::config::constants::UNTAGGED_NODES_SPATIAL_RELS_OPTION_LONG,
generateBooleanLiteral(_config.addSpatialRelsForUntaggedNodes));

writeOptionTriple(osm2rdf::config::constants::BLANK_NODES_OPTION_LONG,
generateBooleanLiteral(_config.noBlankNodes));
writeOptionTriple(osm2rdf::config::constants::NO_UNTAGGED_NODES_OPTION_LONG,
generateBooleanLiteral(!_config.addUntaggedNodes));
writeOptionTriple(osm2rdf::config::constants::NO_UNTAGGED_WAYS_OPTION_LONG,
Expand Down Expand Up @@ -426,6 +429,39 @@ std::string osm2rdf::ttl::Writer<T>::generateBlankNode() {
std::to_string(_blankNodeCount[threadId]++);
}

// ____________________________________________________________________________
template <typename T>
std::string osm2rdf::ttl::Writer<T>::generateSkolem(const std::string& id) {
return generateIRIUnsafe(osm2rdf::ttl::constants::NAMESPACE__GENID, id);
}

// ____________________________________________________________________________
template <typename T>
std::string osm2rdf::ttl::Writer<T>::generateSkolemForRelationMember(
const uint64_t& relationId,
const uint64_t& memberId,
const std::string& memberType,
const size_t& relPos) {
const std::string skolemId = "r" + std::to_string(relationId) +
//Extract the first relevant char to determine
//the type of the osm/ohm object
memberType.at(3) + std::to_string(memberId) +
"p" + std::to_string(relPos);
return generateSkolem(skolemId);
}

// ____________________________________________________________________________
template <typename T>
std::string osm2rdf::ttl::Writer<T>::generateSkolemForWayMember(
const uint64_t& wayId,
const uint64_t& nodeId,
const size_t& relPos) {
const std::string skolemId = "w" + std::to_string(wayId) +
"n" + std::to_string(nodeId) +
"p" + std::to_string(relPos);
return generateSkolem(skolemId);
}

// ____________________________________________________________________________
template <typename T>
void osm2rdf::ttl::Writer<T>::writeIRI(std::string_view p, uint64_t v,
Expand Down
28 changes: 28 additions & 0 deletions tests/config/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ void assertDefaultConfig(const osm2rdf::config::Config& config) {

ASSERT_FALSE(config.writeRDFStatistics);

ASSERT_FALSE(config.noBlankNodes);

ASSERT_EQ(0, config.simplifyGeometries);
ASSERT_EQ(0, config.simplifyWKT);
ASSERT_EQ(5, config.wktDeviation);
Expand Down Expand Up @@ -658,6 +660,21 @@ TEST(CONFIG_Config, fromArgsSimplifyWKTLong) {
ASSERT_EQ(25, config.simplifyWKT);
}

// ____________________________________________________________________________
TEST(CONFIG_Config, fromArgsNoBlankNodesLong) {
osm2rdf::config::Config config;
assertDefaultConfig(config);
osm2rdf::util::CacheFile cf("/tmp/dummyInput");

const auto arg = "--" + osm2rdf::config::constants::BLANK_NODES_OPTION_LONG;
const int argc = 3;
char* argv[argc] = {const_cast<char*>(""), const_cast<char*>(arg.c_str()),
const_cast<char*>("/tmp/dummyInput")};
config.fromArgs(argc, argv);
ASSERT_EQ("", config.output.string());
ASSERT_TRUE(config.noBlankNodes);
}

// ____________________________________________________________________________
TEST(CONFIG_Config, fromArgsSimplifyWKTDeviationLong) {
osm2rdf::config::Config config;
Expand Down Expand Up @@ -928,6 +945,17 @@ TEST(CONFIG_Config, getInfoSimplifyWKT) {
res, ::testing::HasSubstr(osm2rdf::config::constants::SIMPLIFY_WKT_INFO));
}

// ____________________________________________________________________________
TEST(CONFIG_Config, getInfoNoBlankNodes) {
osm2rdf::config::Config config;
assertDefaultConfig(config);
config.noBlankNodes = true;

const std::string res = config.getInfo("");
ASSERT_THAT(
res, ::testing::HasSubstr(osm2rdf::config::constants::BLANK_NODES_INFO));
}

// ____________________________________________________________________________
TEST(CONFIG_Config, getInfoSkipWikiLinks) {
osm2rdf::config::Config config;
Expand Down
54 changes: 54 additions & 0 deletions tests/ttl/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,60 @@ TEST(TTL_WriterQLEVER, generateBlankNode) {
}
}

// ____________________________________________________________________________
TEST(TTL_WriterQLEVER, generateSkolem) {
osm2rdf::config::Config config;
osm2rdf::ttl::Writer<osm2rdf::ttl::format::QLEVER> w{config, nullptr};
{
const std::string res = w.generateSkolem("1");
ASSERT_STREQ("genid:1", res.c_str());
}
{
const std::string res = w.generateSkolem("2");
ASSERT_STREQ("genid:2", res.c_str());
}
{
const std::string res = w.generateSkolem("3");
ASSERT_STREQ("genid:3", res.c_str());
}
}

// ____________________________________________________________________________
TEST(TTL_WriterQLEVER, generateSkolemForRelationMember) {
osm2rdf::config::Config config;
osm2rdf::ttl::Writer<osm2rdf::ttl::format::QLEVER> w{config, nullptr};
{
const std::string res = w.generateSkolemForRelationMember(1, 2, "osmnode", 3);
ASSERT_STREQ("genid:r1n2p3", res.c_str());
}
{
const std::string res = w.generateSkolemForRelationMember(1, 2, "osmway", 3);
ASSERT_STREQ("genid:r1w2p3", res.c_str());
}
{
const std::string res = w.generateSkolemForRelationMember(1, 2, "ohmnode", 3);
ASSERT_STREQ("genid:r1n2p3", res.c_str());
}
}

// ____________________________________________________________________________
TEST(TTL_WriterQLEVER, generateSkolemForWayMember) {
osm2rdf::config::Config config;
osm2rdf::ttl::Writer<osm2rdf::ttl::format::QLEVER> w{config, nullptr};
{
const std::string res = w.generateSkolemForWayMember(1, 1, 1);
ASSERT_STREQ("genid:w1n1p1", res.c_str());
}
{
const std::string res = w.generateSkolemForWayMember(2, 3, 4);
ASSERT_STREQ("genid:w2n3p4", res.c_str());
}
{
const std::string res = w.generateSkolemForWayMember(3, 5, 7);
ASSERT_STREQ("genid:w3n5p7", res.c_str());
}
}

// ____________________________________________________________________________
TEST(TTL_WriterNT, generateIRI_ID) {
osm2rdf::config::Config config;
Expand Down