diff --git a/examples/osmium_to_postgis.cpp b/examples/osmium_to_postgis.cpp index c0eacb8..1606b12 100644 --- a/examples/osmium_to_postgis.cpp +++ b/examples/osmium_to_postgis.cpp @@ -3,6 +3,9 @@ This is an example tool that loads OSM data into a PostGIS database with hstore tags column using the OGR library. + The database must have the HSTORE and POSTGIS extentions + loaded. + */ /* @@ -38,8 +41,9 @@ You should have received a copy of the Licenses along with Osmium. If not, see #define OSMIUM_WITH_XML_INPUT #include -#include -#include +#include +#include +#include #include #include @@ -47,16 +51,16 @@ class MyOGRHandler : public Osmium::Handler::Base { OGRDataSource* m_data_source; OGRLayer* m_layer_point; - Osmium::OSM::TagKeyFilterOp m_filter; - Osmium::OSM::TagToHstoreStringOp m_tohstore; + Osmium::Tags::KeyFilter m_filter; + Osmium::Tags::TagToHStoreStringOp m_tohstore; public: MyOGRHandler(const std::string& filename) : m_data_source(NULL), m_layer_point(NULL), - m_filter(), - m_tohstore(Osmium::OSM::TagToHstoreStringOp()) { + m_filter(true), + m_tohstore() { OGRRegisterAll(); OGRSFDriver* driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("PostgreSQL"); @@ -83,8 +87,8 @@ class MyOGRHandler : public Osmium::Handler::Base { // using transactions make this much faster than without m_layer_point->StartTransaction(); - m_filter.add("created_by"); - m_filter.add("odbl"); + m_filter.add(false, "created_by"); + m_filter.add(false, "odbl"); } ~MyOGRHandler() { @@ -96,7 +100,7 @@ class MyOGRHandler : public Osmium::Handler::Base { if (!node->tags().empty()) { std::string tags; - Osmium::OSM::tags_filter_and_accumulate(node->tags(), m_filter, &tags, m_tohstore); + Osmium::filter_and_accumulate(node->tags(), m_filter, tags, m_tohstore); if (!tags.empty()) { try { diff --git a/include/osmium/osm/tag_filter.hpp b/include/osmium/tags/key_filter.hpp similarity index 52% rename from include/osmium/osm/tag_filter.hpp rename to include/osmium/tags/key_filter.hpp index 47e35d5..1cfb52f 100644 --- a/include/osmium/osm/tag_filter.hpp +++ b/include/osmium/tags/key_filter.hpp @@ -1,5 +1,5 @@ -#ifndef OSMIUM_OSM_TAG_FILTER_HPP -#define OSMIUM_OSM_TAG_FILTER_HPP +#ifndef OSMIUM_TAGS_KEY_FILTER_HPP +#define OSMIUM_TAGS_KEY_FILTER_HPP /* @@ -23,7 +23,6 @@ You should have received a copy of the Licenses along with Osmium. If not, see */ #include -#include #include #include #include @@ -33,48 +32,51 @@ You should have received a copy of the Licenses along with Osmium. If not, see namespace Osmium { - namespace OSM { + namespace Tags { - class TagKeyFilterOp : public std::unary_function { + class KeyFilter : public std::unary_function { + + struct rule_t { + bool result; + std::string key; + + rule_t(bool r, const char* k) : + result(r), + key(k) { + } + + }; + + std::vector m_rules; + bool m_default_result; public: - TagKeyFilterOp() : - m_keys() { + typedef boost::filter_iterator iterator; + + KeyFilter(bool default_result) : + m_rules(), + m_default_result(default_result) { } - TagKeyFilterOp& add(const char* key) { - m_keys.push_back(key); + KeyFilter& add(bool result, const char* key) { + m_rules.push_back(rule_t(result, key)); return *this; } bool operator()(const Osmium::OSM::Tag& tag) const { - BOOST_FOREACH(const std::string& key, m_keys) { - if (key == tag.key()) { - return false; + BOOST_FOREACH(const rule_t& rule, m_rules) { + if (tag.key() == rule.key) { + return rule.result; } } - return true; + return m_default_result; } - private: - - std::vector m_keys; - }; - typedef boost::filter_iterator TagKeyFilterOpIterator; - - template - inline void tags_filter_and_accumulate(const TagList& tags, TagKeyFilterOp& filter, std::string* stringptr, T convert) { - TagKeyFilterOpIterator fi_begin(filter, tags.begin(), tags.end()); - TagKeyFilterOpIterator fi_end(filter, tags.end(), tags.end()); - - std::accumulate(fi_begin, fi_end, stringptr, convert); - } - - } // namespace OSM + } // namespace Tags } // namespace Osmium -#endif // OSMIUM_OSM_TAG_FILTER_HPP +#endif // OSMIUM_TAGS_KEY_FILTER_HPP diff --git a/include/osmium/tags/key_value_filter.hpp b/include/osmium/tags/key_value_filter.hpp new file mode 100644 index 0000000..66714db --- /dev/null +++ b/include/osmium/tags/key_value_filter.hpp @@ -0,0 +1,84 @@ +#ifndef OSMIUM_TAGS_KEY_VALUE_FILTER_HPP +#define OSMIUM_TAGS_KEY_VALUE_FILTER_HPP + +/* + +Copyright 2012 Jochen Topf and others (see README). + +This file is part of Osmium (https://github.com/joto/osmium). + +Osmium is free software: you can redistribute it and/or modify it under the +terms of the GNU Lesser General Public License or (at your option) the GNU +General Public License as published by the Free Software Foundation, either +version 3 of the Licenses, or (at your option) any later version. + +Osmium is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License and the GNU +General Public License for more details. + +You should have received a copy of the Licenses along with Osmium. If not, see +. + +*/ + +#include +#include +#include +#include + +#include +#include + +namespace Osmium { + + namespace Tags { + + class KeyValueFilter : public std::unary_function { + + struct rule_t { + bool result; + std::string key; + std::string value; + + rule_t(bool r, const char* k, const char* v) : + result(r), + key(k), + value(v ? v : "") { + } + + }; + + std::vector m_rules; + bool m_default_result; + + public: + + typedef boost::filter_iterator iterator; + + KeyValueFilter(bool default_result) : + m_rules(), + m_default_result(default_result) { + } + + KeyValueFilter& add(bool result, const char* key, const char* value = NULL) { + m_rules.push_back(rule_t(result, key, value)); + return *this; + } + + bool operator()(const Osmium::OSM::Tag& tag) const { + BOOST_FOREACH(const rule_t &rule, m_rules) { + if (tag.key() == rule.key && (rule.value.empty() || tag.value() == rule.value)) { + return rule.result; + } + } + return m_default_result; + } + + }; + + } // namespace Tags + +} // namespace Osmium + +#endif // OSMIUM_TAGS_KEY_VALUE_FILTER_HPP diff --git a/include/osmium/tags/regex_filter.hpp b/include/osmium/tags/regex_filter.hpp new file mode 100644 index 0000000..ba3e780 --- /dev/null +++ b/include/osmium/tags/regex_filter.hpp @@ -0,0 +1,90 @@ +#ifndef OSMIUM_TAGS_REGEX_FILTER_HPP +#define OSMIUM_TAGS_REGEX_FILTER_HPP + +/* + +Copyright 2012 Jochen Topf and others (see README). + +This file is part of Osmium (https://github.com/joto/osmium). + +Osmium is free software: you can redistribute it and/or modify it under the +terms of the GNU Lesser General Public License or (at your option) the GNU +General Public License as published by the Free Software Foundation, either +version 3 of the Licenses, or (at your option) any later version. + +Osmium is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License and the GNU +General Public License for more details. + +You should have received a copy of the Licenses along with Osmium. If not, see +. + +*/ + +#define OSMIUM_LINK_WITH_LIBS_GEOS -lboost_regex + +#include +#include +#include +#include +#include + +#include +#include + +namespace Osmium { + + namespace Tags { + + class RegexFilter : public std::unary_function { + + struct rule_t { + bool result; + boost::regex key; + boost::regex value; + + rule_t(bool r, const char* k, const char* v) : + result(r), + key(k), + value() { + if (v) { + value = v; + } + } + + }; + + std::vector m_rules; + bool m_default_result; + + public: + + typedef boost::filter_iterator iterator; + + RegexFilter(bool default_result) : + m_rules(), + m_default_result(default_result) { + } + + RegexFilter& add(bool result, const char* key, const char* value = NULL) { + m_rules.push_back(rule_t(result, key, value)); + return *this; + } + + bool operator()(const Osmium::OSM::Tag& tag) const { + BOOST_FOREACH(const rule_t &rule, m_rules) { + if (boost::regex_match(tag.key(), rule.key) && (rule.value.empty() || boost::regex_match(tag.value(), rule.value))) { + return rule.result; + } + } + return m_default_result; + } + + }; + + } // namespace Tags + +} // namespace Osmium + +#endif // OSMIUM_TAGS_REGEX_FILTER_HPP diff --git a/include/osmium/osm/tag_list_to_string.hpp b/include/osmium/tags/to_string.hpp similarity index 53% rename from include/osmium/osm/tag_list_to_string.hpp rename to include/osmium/tags/to_string.hpp index 22c4dd7..78f0fea 100644 --- a/include/osmium/osm/tag_list_to_string.hpp +++ b/include/osmium/tags/to_string.hpp @@ -1,5 +1,5 @@ -#ifndef OSMIUM_OSM_TAG_LIST_TO_STRING_HPP -#define OSMIUM_OSM_TAG_LIST_TO_STRING_HPP +#ifndef OSMIUM_TAGS_TAG_LIST_TO_STRING_HPP +#define OSMIUM_TAGS_TAG_LIST_TO_STRING_HPP /* @@ -29,9 +29,18 @@ You should have received a copy of the Licenses along with Osmium. If not, see namespace Osmium { - namespace OSM { + namespace Tags { - class TagToStringOp : public std::binary_function { + /** + * Operation that turns a Tag into a string with many parameters that define how this is done. + * + * @parameter escape String that contains all characters that should be escaped with a backslash (\) + * @parameter prefix String printed before a tag. + * @parameter infix String printed between key and value of a tag. + * @parameter suffix String printed after a tag. + * @parameter join String used to join several tags together. + */ + class TagToStringOp : public std::binary_function { public: @@ -43,16 +52,16 @@ namespace Osmium { m_join(join) { } - std::string* operator()(std::string* string, const Osmium::OSM::Tag& tag) const { - if (!string->empty()) { - string->append(m_join); + std::string& operator()(std::string& output, const Osmium::OSM::Tag& tag) const { + if (!output.empty()) { + output.append(m_join); } - string->append(m_prefix); - append_escaped_string(string, tag.key()); - string->append(m_infix); - append_escaped_string(string, tag.value()); - string->append(m_suffix); - return string; + output.append(m_prefix); + append_escaped_string(output, tag.key()); + output.append(m_infix); + append_escaped_string(output, tag.value()); + output.append(m_suffix); + return output; } private: @@ -63,17 +72,20 @@ namespace Osmium { const char* m_suffix; const char* m_join; - void append_escaped_string(std::string* out, const char* in) const { + void append_escaped_string(std::string& output, const char* in) const { while (*in) { if (m_escape.find(*in) != std::string::npos) { - out->append(1, '\\'); + output.append(1, '\\'); } - out->append(1, *in++); + output.append(1, *in++); } } - }; + }; // class TagToStringOp + /** + * Operation that turns a Tag into a string in the format "key=value". + */ class TagToKeyEqualsValueStringOp : public TagToStringOp { public: @@ -82,20 +94,23 @@ namespace Osmium { TagToStringOp("", "", "=", "", join) { } - }; + }; // class TagToKeyEqualsValueStringOp - class TagToHstoreStringOp : public TagToStringOp { + /** + * Operation that turns a Tag into a string in the format used for the hstore PostgreSQL extension. + */ + class TagToHStoreStringOp : public TagToStringOp { public: - TagToHstoreStringOp() : + TagToHStoreStringOp() : TagToStringOp("\\\"", "\"", "\"=>\"", "\"", ",") { } - }; + }; // class TagToHStoreStringOp - } // namespace OSM + } // namespace Tags } // namespace Osmium -#endif // OSMIUM_OSM_TAG_LIST_TO_STRING_HPP +#endif // OSMIUM_TAGS_TAG_LIST_TO_STRING_HPP diff --git a/include/osmium/utils/filter_and_accumulate.hpp b/include/osmium/utils/filter_and_accumulate.hpp new file mode 100644 index 0000000..84688e2 --- /dev/null +++ b/include/osmium/utils/filter_and_accumulate.hpp @@ -0,0 +1,48 @@ +#ifndef OSMIUM_UTILS_FILTER_AND_ACCUMULATE_HPP +#define OSMIUM_UTILS_FILTER_AND_ACCUMULATE_HPP + +/* + +Copyright 2012 Jochen Topf and others (see README). + +This file is part of Osmium (https://github.com/joto/osmium). + +Osmium is free software: you can redistribute it and/or modify it under the +terms of the GNU Lesser General Public License or (at your option) the GNU +General Public License as published by the Free Software Foundation, either +version 3 of the Licenses, or (at your option) any later version. + +Osmium is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License and the GNU +General Public License for more details. + +You should have received a copy of the Licenses along with Osmium. If not, see +. + +*/ + +#include + +namespace Osmium { + + /** + * Similar to the std::accumulate function, but first filters while + * iterating over the container. + * + * @parameter container Some container class, must support begin() and end() functions. + * @parameter filter Filter class, must support operator() that takes a member of the container and returns bool. + * @parameter init Initial value for accumulation. + * @parameter binary_op Operation called when accumulating. + */ + template + inline void filter_and_accumulate(TContainer& container, TFilter& filter, TAccum init, TBinaryOp binary_op) { + typename TFilter::iterator fi_begin(filter, container.begin(), container.end()); + typename TFilter::iterator fi_end(filter, container.end(), container.end()); + + std::accumulate(fi_begin, fi_end, init, binary_op); + } + +} // namespace Osmium + +#endif // OSMIUM_UTILS_FILTER_AND_ACCUMULATE_HPP diff --git a/test/t/tags/test_filter.cpp b/test/t/tags/test_filter.cpp new file mode 100644 index 0000000..c2e3dac --- /dev/null +++ b/test/t/tags/test_filter.cpp @@ -0,0 +1,111 @@ +#define BOOST_TEST_DYN_LINK +#ifdef STAND_ALONE +# define BOOST_TEST_MODULE Main +#endif +#include + +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(Filter) + +BOOST_AUTO_TEST_CASE(key_filter) { + Osmium::Tags::KeyFilter filter(false); + filter.add(true, "highway"); + filter.add(true, "name"); + + BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary"))); + BOOST_CHECK(!filter(Osmium::OSM::Tag("blurb", "flurb"))); + + Osmium::OSM::TagList tags; + tags.add("highway", "residential"); + tags.add("oneway", "yes"); + tags.add("name", "Main Street"); + + Osmium::Tags::KeyFilter::iterator fi_begin(filter, tags.begin(), tags.end()); + Osmium::Tags::KeyFilter::iterator fi_end(filter, tags.end(), tags.end()); + + BOOST_CHECK(fi_begin != fi_end); + BOOST_CHECK_EQUAL(Osmium::OSM::Tag("highway", "residential"), *fi_begin++); + BOOST_CHECK(fi_begin != fi_end); + BOOST_CHECK_EQUAL(Osmium::OSM::Tag("name", "Main Street"), *fi_begin++); + BOOST_CHECK(fi_begin == fi_end); +} + +BOOST_AUTO_TEST_CASE(key_value_filter) { + Osmium::Tags::KeyValueFilter filter(false); + filter.add(true, "highway", "motorway"); + filter.add(true, "highway", "trunk"); + filter.add(true, "highway", "primary"); + + BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary"))); + BOOST_CHECK(!filter(Osmium::OSM::Tag("blurb", "flurb"))); + BOOST_CHECK(!filter(Osmium::OSM::Tag("highway", "residential"))); + + { + Osmium::OSM::TagList tags; + tags.add("highway", "residential"); + tags.add("name", "Main Street"); + + Osmium::Tags::KeyValueFilter::iterator fi_begin(filter, tags.begin(), tags.end()); + Osmium::Tags::KeyValueFilter::iterator fi_end(filter, tags.end(), tags.end()); + + BOOST_CHECK(fi_begin == fi_end); + } + + { + Osmium::OSM::TagList tags; + tags.add("highway", "primary"); + tags.add("name", "Main Street"); + + Osmium::Tags::KeyValueFilter::iterator fi_begin(filter, tags.begin(), tags.end()); + Osmium::Tags::KeyValueFilter::iterator fi_end(filter, tags.end(), tags.end()); + + BOOST_CHECK(fi_begin != fi_end); + BOOST_CHECK_EQUAL(Osmium::OSM::Tag("highway", "primary"), *fi_begin++); + BOOST_CHECK(fi_begin == fi_end); + } +} + +BOOST_AUTO_TEST_CASE(key_value_filter_empty) { + Osmium::Tags::KeyValueFilter filter(false); + filter.add(true, "highway", ""); + + BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary"))); + BOOST_CHECK(!filter(Osmium::OSM::Tag("blurb", "flurb"))); + BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "residential"))); + + Osmium::OSM::TagList tags; + tags.add("highway", "primary"); + tags.add("name", "Main Street"); + + Osmium::Tags::KeyValueFilter::iterator fi_begin(filter, tags.begin(), tags.end()); + Osmium::Tags::KeyValueFilter::iterator fi_end(filter, tags.end(), tags.end()); + + BOOST_CHECK(fi_begin != fi_end); + BOOST_CHECK_EQUAL(Osmium::OSM::Tag("highway", "primary"), *fi_begin++); + BOOST_CHECK(fi_begin == fi_end); +} + +BOOST_AUTO_TEST_CASE(key_value_filter_null) { + Osmium::Tags::KeyValueFilter filter(false); + filter.add(true, "highway"); + + BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary"))); + BOOST_CHECK(!filter(Osmium::OSM::Tag("blurb", "flurb"))); + BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "residential"))); +} + +BOOST_AUTO_TEST_CASE(key_value_filter_tf) { + Osmium::Tags::KeyValueFilter filter(false); + filter.add(false, "highway", "residential"); + filter.add(true, "highway"); + + BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary"))); + BOOST_CHECK(!filter(Osmium::OSM::Tag("blurb", "flurb"))); + BOOST_CHECK(!filter(Osmium::OSM::Tag("highway", "residential"))); +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/test/t/tags/test_regex_filter.cpp b/test/t/tags/test_regex_filter.cpp new file mode 100644 index 0000000..934c467 --- /dev/null +++ b/test/t/tags/test_regex_filter.cpp @@ -0,0 +1,39 @@ +#define BOOST_TEST_DYN_LINK +#ifdef STAND_ALONE +# define BOOST_TEST_MODULE Main +#endif +#include + +#include +#include + +BOOST_AUTO_TEST_SUITE(RegexFilter) + +BOOST_AUTO_TEST_CASE(regex_filter) { + Osmium::Tags::RegexFilter filter(false); + filter.add(true, "^highway$", "^(motorway|trunk|primary)(_link)?$"); + filter.add(true, "^highway$", "^residential$"); + filter.add(true, "^oneway$"); + + BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary"))); + BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "primary_link"))); + BOOST_CHECK(!filter(Osmium::OSM::Tag("blurb", "flurb"))); + BOOST_CHECK(filter(Osmium::OSM::Tag("highway", "residential"))); + BOOST_CHECK(filter(Osmium::OSM::Tag("oneway", "yes"))); + + { + Osmium::OSM::TagList tags; + tags.add("highway", "residential"); + tags.add("name", "Main Street"); + + Osmium::Tags::RegexFilter::iterator fi_begin(filter, tags.begin(), tags.end()); + Osmium::Tags::RegexFilter::iterator fi_end(filter, tags.end(), tags.end()); + + BOOST_CHECK(fi_begin != fi_end); + BOOST_CHECK_EQUAL(Osmium::OSM::Tag("highway", "residential"), *fi_begin++); + BOOST_CHECK(fi_begin == fi_end); + } +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/test/t/tags/test_to_string.cpp b/test/t/tags/test_to_string.cpp new file mode 100644 index 0000000..f060e6e --- /dev/null +++ b/test/t/tags/test_to_string.cpp @@ -0,0 +1,52 @@ +#define BOOST_TEST_DYN_LINK +#ifdef STAND_ALONE +# define BOOST_TEST_MODULE Main +#endif +#include + +#include +#include + +BOOST_AUTO_TEST_SUITE(TagToString) + +BOOST_AUTO_TEST_CASE(tag_to_string) { + Osmium::OSM::Tag t1("highway", "primary"); + Osmium::OSM::Tag t2("name", "Main Street"); + + { + std::string out; + Osmium::Tags::TagToStringOp op("", "PREFIX", "INFIX", "SUFFIX", "JOIN"); + op(out, t1); + op(out, t2); + BOOST_CHECK_EQUAL("PREFIXhighwayINFIXprimarySUFFIXJOINPREFIXnameINFIXMain StreetSUFFIX", out); + } + + { + std::string out; + Osmium::Tags::TagToKeyEqualsValueStringOp op(","); + op(out, t1); + op(out, t2); + BOOST_CHECK_EQUAL("highway=primary,name=Main Street", out); + } + + { + std::string out; + Osmium::Tags::TagToHStoreStringOp op; + op(out, t1); + op(out, t2); + BOOST_CHECK_EQUAL("\"highway\"=>\"primary\",\"name\"=>\"Main Street\"", out); + } + +} + +BOOST_AUTO_TEST_CASE(escape) { + std::string out; + Osmium::OSM::Tag t1("name", "O'Rourke Street (\"Fool's Corner\")"); + + Osmium::Tags::TagToHStoreStringOp op; + op(out, t1); + BOOST_CHECK_EQUAL("\"name\"=>\"O'Rourke Street (\\\"Fool's Corner\\\")\"", out); +} + +BOOST_AUTO_TEST_SUITE_END() +