[pyosmium] 01/02: Imported Upstream version 0.0~20150204-298c708

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Fri Mar 6 17:09:30 UTC 2015


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository pyosmium.

commit 3519db65c8c7885fed4d98d3b7ea0d7a3979a875
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Fri Mar 6 15:28:31 2015 +0100

    Imported Upstream version 0.0~20150204-298c708
---
 .gitignore                   |   2 +
 .travis.yml                  |  43 ++++++++++++
 LICENSE.TXT                  |  25 +++++++
 README.md                    |  54 +++++++++++++++
 examples/amenity_list.py     |  27 ++++++++
 examples/create_nodecache.py |  15 ++++
 examples/osm_file_stats.py   |  26 +++++++
 examples/pub_names.py        |  19 +++++
 examples/road_length.py      |  19 +++++
 examples/use_nodecache.py    |  28 ++++++++
 lib/generic_handler.hpp      | 161 +++++++++++++++++++++++++++++++++++++++++++
 lib/geom.cc                  |  30 ++++++++
 lib/index.cc                 |  33 +++++++++
 lib/io.cc                    |  24 +++++++
 lib/osm.cc                   | 117 +++++++++++++++++++++++++++++++
 lib/osmium.cc                |  86 +++++++++++++++++++++++
 osmium/__init__.py           |   5 ++
 setup.py                     |  57 +++++++++++++++
 test/run_tests.py            |  16 +++++
 test/test_helper.py          |  88 +++++++++++++++++++++++
 test/test_io.py              |  64 +++++++++++++++++
 test/test_osm.py             |  75 ++++++++++++++++++++
 22 files changed, 1014 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6f97ca1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+build
+*.pyc
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..d855c02
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,43 @@
+language: cpp
+
+compiler:
+ - gcc
+ - clang
+
+env:
+ - USE_PYTHON_VERSION=2
+ - USE_PYTHON_VERSION=3
+
+before_install:
+ # we need at least g++-4.8 for c++11 features
+ - sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
+ - sudo apt-get update --yes --quiet
+
+install:
+ # upgrade compilers
+ - sudo apt-get install --yes gcc-4.8 g++-4.8
+ # make sure 'cpp' is the just installed current one
+ - sudo rm /usr/bin/cpp
+ - sudo ln -s /usr/bin/cpp-4.8 /usr/bin/cpp
+ # innstall dependencies
+ - sudo apt-get install --yes make python3-dev python3 libboost-dev libboost-python-dev libprotobuf-dev protobuf-compiler libsparsehash-dev python-nose python3-nose
+ - cd ..
+ - git clone https://github.com/osmcode/libosmium.git
+ # OSMPBF is too old, install from git
+ #- sudo apt-get install --yes libosmpbf-dev
+ - git clone https://github.com/scrosby/OSM-binary.git
+ - cd OSM-binary/src
+ - make
+ - sudo make install
+
+#before_script:
+# - true
+
+script:
+ - cd $TRAVIS_BUILD_DIR
+ - if [ "${CXX}" = 'g++' ]; then export CXX=g++-4.8; fi;
+ - if [ "${CC}" = 'gcc' ]; then export CC=gcc-4.8; fi;
+ - python${USE_PYTHON_VERSION} setup.py build
+ - cd test
+ - python${USE_PYTHON_VERSION} run_tests.py
+
diff --git a/LICENSE.TXT b/LICENSE.TXT
new file mode 100644
index 0000000..fdccd14
--- /dev/null
+++ b/LICENSE.TXT
@@ -0,0 +1,25 @@
+
+Copyright (c) 2014, Sarah Hoffmann
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0a82bad
--- /dev/null
+++ b/README.md
@@ -0,0 +1,54 @@
+# pyosmium
+
+Provides Python bindings for the [libosmium](https://github.com/osmcode/libosmium) C++
+library, a library for working with OpenStreetMap data in a fast and flexible
+manner.
+
+[![Build Status](https://secure.travis-ci.org/osmcode/pyosmium.png)](http://travis-ci.org/osmcode/pyosmium)
+
+## Depends
+
+Python >= 2.7 is supported (that includes python 3.x).
+
+pyosmium uses [Boost.Python](http://www.boost.org/doc/libs/1_56_0/libs/python/doc/index.html)
+to create the bindings. On Debian/Ubuntu install `libboost-python-dev`.
+
+## Installation
+
+To compile the bindings, run
+
+    python setup.py build
+
+To compile and install the bindings, run
+
+    python setup.py install
+
+libosmium is expected to reside in the same directory as pyosmium or to be
+installed globally.
+
+## Examples
+
+The `example` directory contains small examples on how to use the library.
+They are for most parts ports of the examples in Libosmium and osmium-contrib.
+
+## Testing
+
+There is a small test suite in the test directory. This provides regression
+test for the python bindings, it is not meant to be a test suite for libosmium.
+
+You'll need the Python `nose` module. On Debian/Ubuntu install the package
+`python-nose`.
+
+The suite can be run with:
+
+    cd test
+    python run_tests.py
+
+## License
+
+Pyosmium is available under the BSD 2-Clause License. See LICENSE.TXT.
+
+## Authors
+
+Sarah Hoffmann (lonvia at denofr.de)
+
diff --git a/examples/amenity_list.py b/examples/amenity_list.py
new file mode 100644
index 0000000..f3bc950
--- /dev/null
+++ b/examples/amenity_list.py
@@ -0,0 +1,27 @@
+import osmium as o
+import sys
+import shapely.wkb as wkblib
+
+wkbfab = o.geom.WKBFactory()
+
+class AmenityListHandler(o.SimpleHandler):
+
+    def print_amenity(amenity, tags, lon, lat):
+        name = tags['name'] if 'name' in tags else ''
+        print("%f %f %-15s %s" % (lon, lat, tags['amenity'], name))
+
+    def node(self, n):
+        if 'amenity' in n.tags:
+            self.print_amenity(n.tags, n.location.lon, n.location.lat)
+
+    def area(self, a):
+        if 'amenity' in a.tags:
+            wkb = wkbfab.create_multipolygon(a)
+            poly = wkblib.loads(wkb, hex=True)
+            centroid = poly.representative_point()
+            self.print_amenity(a.tags, centroid.x, centroid.y)
+
+
+handler = AmenityListHandler()
+
+handler.apply_file(sys.argv[1])
diff --git a/examples/create_nodecache.py b/examples/create_nodecache.py
new file mode 100644
index 0000000..8e9ac55
--- /dev/null
+++ b/examples/create_nodecache.py
@@ -0,0 +1,15 @@
+import osmium as o
+import sys
+
+if len(sys.argv) != 3:
+    print("Usage: python create_nodecache.py <osm file> <node cache>")
+    exit()
+
+reader = o.io.Reader(sys.argv[1], o.osm.osm_entity_bits.NODE)
+
+idx = o.index.create_map("sparse_file_array," + sys.argv[2])
+lh = o.NodeLocationsForWays(idx)
+
+o.apply(reader, lh)
+
+reader.close()
diff --git a/examples/osm_file_stats.py b/examples/osm_file_stats.py
new file mode 100644
index 0000000..c5dcf09
--- /dev/null
+++ b/examples/osm_file_stats.py
@@ -0,0 +1,26 @@
+import osmium as o
+import sys
+
+class FileStatsHandler(o.SimpleHandler):
+    def __init__(self):
+        o.SimpleHandler.__init__(self)
+        self.nodes = 0
+        self.ways = 0
+        self.rels = 0
+
+    def node(self, n):
+        self.nodes += 1
+
+    def way(self, w):
+        self.ways += 1
+
+    def relation(self, r):
+        self.rels += 1
+
+
+h = FileStatsHandler()
+h.apply_file(sys.argv[1])
+
+print("Nodes: %d" % h.nodes)
+print("Ways: %d" % h.ways)
+print("Relations: %d" % h.rels)
diff --git a/examples/pub_names.py b/examples/pub_names.py
new file mode 100644
index 0000000..69d4f9d
--- /dev/null
+++ b/examples/pub_names.py
@@ -0,0 +1,19 @@
+import osmium
+import sys
+
+class NamesHandler(osmium.SimpleHandler):
+
+    def output_pubs(self, tags):
+        if 'amenity' in tags and tags['amenity'] == 'pub':
+            if 'name' in tags:
+                print(tags['name'])
+
+    def node(self, n):
+        self.output_pubs(n.tags)
+
+    def way(self, w):
+        self.output_pubs(w.tags)
+
+h = NamesHandler()
+h.apply_file(sys.argv[1])
+
diff --git a/examples/road_length.py b/examples/road_length.py
new file mode 100644
index 0000000..915336d
--- /dev/null
+++ b/examples/road_length.py
@@ -0,0 +1,19 @@
+import osmium as o
+import sys
+
+class RoadLengthHandler(o.SimpleHandler):
+    def __init__(self):
+        o.SimpleHandler.__init__(self)
+        self.length = 0.0
+
+    def way(self, w):
+        if 'highway' in w.tags:
+            try:
+                self.length += o.geom.haversine_distance(w.nodes)
+            except o.InvalidLocationError:
+                print("WARNING: way %d incomplete. Ignoring." % w.id)
+
+h = RoadLengthHandler()
+h.apply_file(sys.argv[1], locations=True)
+
+print('Total way length: %.2f km' % (h.length/1000))
diff --git a/examples/use_nodecache.py b/examples/use_nodecache.py
new file mode 100644
index 0000000..e819257
--- /dev/null
+++ b/examples/use_nodecache.py
@@ -0,0 +1,28 @@
+import osmium as o
+import sys
+
+class WayHandler(o.SimpleHandler):
+
+    def __init__(self, idx):
+        o.SimpleHandler.__init__(self)
+        self.idx = idx
+
+    def way(self, w):
+        for n in w.nodes:
+            n.lat, n.lon # throws an exception if the coordinates are missing
+            loc = idx.get(n.ref)
+        print("%d %s" %(w.id, len(w.nodes)))
+
+if len(sys.argv) != 3:
+    print("Usage: python create_nodecache.py <osm file> <node cache>")
+    exit()
+
+reader = o.io.Reader(sys.argv[1], o.osm.osm_entity_bits.WAY)
+
+idx = o.index.create_map("sparse_file_array," + sys.argv[2])
+lh = o.NodeLocationsForWays(idx)
+lh.ignore_errors()
+
+o.apply(reader, lh, WayHandler(idx))
+
+reader.close()
diff --git a/lib/generic_handler.hpp b/lib/generic_handler.hpp
new file mode 100644
index 0000000..b15c8d0
--- /dev/null
+++ b/lib/generic_handler.hpp
@@ -0,0 +1,161 @@
+#include <osmium/handler.hpp>
+#include <osmium/index/map/all.hpp>
+
+using namespace boost::python;
+
+typedef osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location> index_type;
+
+class BaseHandler : public osmium::handler::Handler {
+
+protected:
+    enum pre_handler {
+        no_handler,
+        location_handler,
+        area_handler
+    };
+
+public:
+// handler functions
+virtual void node(const osmium::Node&) const = 0;
+virtual void way(const osmium::Way&) const = 0;
+virtual void relation(const osmium::Relation&) const = 0;
+virtual void changeset(const osmium::Changeset&) const = 0;
+virtual void area(const osmium::Area&) const = 0;
+
+
+private:
+void apply_with_location(osmium::io::Reader &r, const std::string &idx) {
+    const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
+    std::unique_ptr<index_type> index = map_factory.create_map(idx);
+    osmium::handler::NodeLocationsForWays<index_type> location_handler(*index);
+    location_handler.ignore_errors();
+    osmium::apply(r, location_handler, *this);
+}
+
+void apply_with_area(osmium::io::Reader &r,
+                     osmium::area::MultipolygonCollector<osmium::area::Assembler> &collector,
+                     const std::string &idx) {
+    const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
+    std::unique_ptr<index_type> index = map_factory.create_map(idx);
+    osmium::handler::NodeLocationsForWays<index_type> location_handler(*index);
+    location_handler.ignore_errors();
+
+    osmium::apply(r, location_handler, *this,
+                  collector.handler([this](const osmium::memory::Buffer& area_buffer) {
+                       osmium::apply(area_buffer, *this);
+                  })
+                 );
+}
+
+
+protected:
+void apply(const std::string &filename, osmium::osm_entity_bits::type types,
+           pre_handler pre = no_handler,
+           const std::string &idx = "sparse_mem_array") {
+
+    switch (pre) {
+    case no_handler:
+        {
+            osmium::io::Reader reader(filename, types);
+            osmium::apply(reader, *this);
+            reader.close();
+            break;
+        }
+    case location_handler:
+        {
+            osmium::io::Reader reader(filename, types);
+            apply_with_location(reader, idx);
+            reader.close();
+            break;
+        }
+    case area_handler:
+        {
+            osmium::area::Assembler::config_type assembler_config;
+            osmium::area::MultipolygonCollector<osmium::area::Assembler> collector(assembler_config);
+
+            osmium::io::Reader reader1(filename);
+            collector.read_relations(reader1);
+            reader1.close();
+
+            osmium::io::Reader reader2(filename);
+            apply_with_area(reader2, collector, idx);
+            reader2.close();
+            break;
+        }
+    }
+}
+
+
+
+};
+
+
+struct SimpleHandlerWrap: BaseHandler, wrapper<BaseHandler> {
+
+    void node(const osmium::Node& node) const {
+        if (override f = this->get_override("node"))
+            f(boost::ref(node));
+    }
+
+    void default_node(const osmium::Node&) const {
+    }
+
+    void way(const osmium::Way& way) const {
+        if (override f = this->get_override("way"))
+            f(boost::ref(way));
+    }
+
+    void default_way(const osmium::Way&) const {
+    }
+
+    void relation(const osmium::Relation& rel) const {
+        if (override f = this->get_override("relation"))
+            f(boost::ref(rel));
+    }
+
+    void default_relation(const osmium::Relation&) const {
+    }
+
+    void changeset(const osmium::Changeset& cs) const {
+        if (override f = this->get_override("changeset"))
+            f(boost::ref(cs));
+    }
+
+    void default_changeset(const osmium::Changeset&) const {
+    }
+
+    void area(const osmium::Area& area) const {
+        if (override f = this->get_override("area"))
+            f(boost::ref(area));
+    }
+
+    void default_area(const osmium::Area&) const {
+    }
+
+    void apply_file(const std::string &filename, bool locations = false,
+                    const std::string &idx = "sparse_mem_array")
+    {
+        osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::nothing;
+        BaseHandler::pre_handler handler = locations?
+                                            BaseHandler::location_handler
+                                            :BaseHandler::no_handler;
+
+        if (this->get_override("area"))
+        {
+            entities = osmium::osm_entity_bits::object;
+            handler = BaseHandler::area_handler;
+        } else {
+            if (locations || this->get_override("node"))
+                entities |= osmium::osm_entity_bits::node;
+            if (this->get_override("way"))
+                entities |= osmium::osm_entity_bits::way;
+            if (this->get_override("relation"))
+                entities |= osmium::osm_entity_bits::relation;
+        }
+
+        if (this->get_override("changeset"))
+            entities |= osmium::osm_entity_bits::changeset;
+
+        apply(filename, entities, handler, idx);
+    }
+};
diff --git a/lib/geom.cc b/lib/geom.cc
new file mode 100644
index 0000000..4da4c0c
--- /dev/null
+++ b/lib/geom.cc
@@ -0,0 +1,30 @@
+#include <boost/python.hpp>
+
+#include <osmium/geom/haversine.hpp>
+#include <osmium/geom/factory.hpp>
+#include <osmium/geom/wkb.hpp>
+
+class WKBFactory : public osmium::geom::WKBFactory<> {
+
+public:
+    WKBFactory()
+    : osmium::geom::WKBFactory<>(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex)
+    {}
+};
+
+BOOST_PYTHON_MODULE(_geom)
+{
+    using namespace boost::python;
+    def("haversine_distance", static_cast<double (*)(const osmium::WayNodeList&)>(&osmium::geom::haversine::distance));
+
+    class_<WKBFactory>("WKBFactory")
+        .add_property("epsg", &WKBFactory::epsg)
+        .add_property("proj_string", &WKBFactory::proj_string)
+        .def("create_point", static_cast<std::string (WKBFactory::*)(const osmium::Location) const>(&WKBFactory::create_point))
+        .def("create_point", static_cast<std::string (WKBFactory::*)(const osmium::Node&)>(&WKBFactory::create_point))
+        .def("create_point", static_cast<std::string (WKBFactory::*)(const osmium::NodeRef&)>(&WKBFactory::create_point))
+        .def("create_linestring", static_cast<std::string (WKBFactory::*)(const osmium::WayNodeList&, osmium::geom::use_nodes, osmium::geom::direction)>(&WKBFactory::create_linestring))
+        .def("create_linestring", static_cast<std::string (WKBFactory::*)(const osmium::Way&, osmium::geom::use_nodes, osmium::geom::direction)>(&WKBFactory::create_linestring))
+        .def("create_multipolygon", &WKBFactory::create_multipolygon)
+    ;
+}
diff --git a/lib/index.cc b/lib/index.cc
new file mode 100644
index 0000000..ff66fbf
--- /dev/null
+++ b/lib/index.cc
@@ -0,0 +1,33 @@
+#include <boost/python.hpp>
+
+#include <osmium/osm.hpp>
+#include <osmium/index/map/all.hpp>
+
+using namespace boost::python;
+
+typedef osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location> LocationTable;
+
+LocationTable *create_map(const std::string& config_string) {
+    const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
+    return map_factory.create_map(config_string).release();
+}
+
+std::vector<std::string> map_types() {
+    const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
+    return map_factory.map_types();
+}
+
+BOOST_PYTHON_MODULE(_index)
+{
+    class_<LocationTable, boost::noncopyable>("LocationTable", no_init)
+        .def("set", &LocationTable::set)
+        .def("get", &LocationTable::get)
+        .def("size", &LocationTable::size)
+        .def("used_memory", &LocationTable::used_memory)
+        .def("clear", &LocationTable::clear)
+    ;
+
+    def("create_map", &create_map, return_value_policy<manage_new_object>());
+    def("map_types", &map_types);
+}
+
diff --git a/lib/io.cc b/lib/io.cc
new file mode 100644
index 0000000..cc45cd1
--- /dev/null
+++ b/lib/io.cc
@@ -0,0 +1,24 @@
+#include <boost/python.hpp>
+
+#include <osmium/io/any_input.hpp>
+
+#include "osm.cc"
+
+BOOST_PYTHON_MODULE(_io)
+{
+    using namespace boost::python;
+
+    class_<osmium::io::Header>("Header")
+        .add_property("has_multiple_object_versions",
+                      &osmium::io::Header::has_multiple_object_versions,
+                      make_function(&osmium::io::Header::set_has_multiple_object_versions, return_value_policy<reference_existing_object>()))
+    ;
+
+    class_<osmium::io::Reader, boost::noncopyable>("Reader", init<std::string>())
+        .def(init<std::string, osmium::osm_entity_bits::type>())
+        .def("eof", &osmium::io::Reader::eof)
+        .def("close", &osmium::io::Reader::close)
+        .def("header", &osmium::io::Reader::header)
+    ;
+
+}
diff --git a/lib/osm.cc b/lib/osm.cc
new file mode 100644
index 0000000..7824a3c
--- /dev/null
+++ b/lib/osm.cc
@@ -0,0 +1,117 @@
+#include <boost/python.hpp>
+
+#include <osmium/osm.hpp>
+#include <osmium/osm/entity_bits.hpp>
+
+inline const char *get_tag_by_key(osmium::TagList const& obj, const char *value)
+{
+    const char* v = obj.get_value_by_key(value);
+    if (!v)
+        PyErr_SetString(PyExc_KeyError, "No tag with that key.");
+    return v;
+}
+
+inline bool taglist_contains_tag(osmium::TagList const& obj, const char *value)
+{
+    const char* v = obj.get_value_by_key(value);
+    return v;
+}
+
+inline const char member_item_type(osmium::RelationMember& obj)
+{
+    return item_type_to_char(obj.type());
+}
+
+
+BOOST_PYTHON_MODULE(_osm)
+{
+    using namespace boost::python;
+
+    enum_<osmium::osm_entity_bits::type>("osm_entity_bits")
+        .value("NOTHING", osmium::osm_entity_bits::nothing)
+        .value("NODE", osmium::osm_entity_bits::node)
+        .value("WAY", osmium::osm_entity_bits::way)
+        .value("RELATION", osmium::osm_entity_bits::relation)
+        .value("AREA", osmium::osm_entity_bits::area)
+        .value("OBJECT", osmium::osm_entity_bits::object)
+        .value("CHANGESET", osmium::osm_entity_bits::changeset)
+        .value("ALL", osmium::osm_entity_bits::all)
+    ;
+    class_<osmium::Timestamp>("Timestamp")
+        .def("__str__", &osmium::Timestamp::to_iso)
+    ;
+    class_<osmium::Location>("Location")
+        .def(init<double, double>())
+        .add_property("x", &osmium::Location::x)
+        .add_property("y", &osmium::Location::y)
+        .add_property("lon", &osmium::Location::lon)
+        .add_property("lat", &osmium::Location::lat)
+        .def("valid", &osmium::Location::valid)
+    ;
+    class_<osmium::Tag, boost::noncopyable>("Tag", no_init)
+        .add_property("k", &osmium::Tag::key)
+        .add_property("v", &osmium::Tag::value)
+    ;
+    class_<osmium::TagList, boost::noncopyable>("TagList", no_init)
+        .def("__len__", &osmium::TagList::size)
+        .def("__getitem__", &get_tag_by_key)
+        .def("__contains__", &taglist_contains_tag)
+        .def("__iter__", iterator<osmium::TagList,return_internal_reference<>>())
+    ;
+    class_<osmium::NodeRef>("NodeRef")
+        .add_property("x", &osmium::NodeRef::x)
+        .add_property("y", &osmium::NodeRef::y)
+        .add_property("lon", &osmium::NodeRef::lon)
+        .add_property("lat", &osmium::NodeRef::lat)
+        .add_property("ref", &osmium::NodeRef::ref)
+        .add_property("location", static_cast<osmium::Location (osmium::NodeRef::*)() const>(&osmium::NodeRef::location))
+    ;
+    class_<osmium::WayNodeList, boost::noncopyable>("WayNodeList", no_init)
+        .def("__len__", &osmium::WayNodeList::size)
+        .def("__getitem__", &osmium::WayNodeList::operator[], return_value_policy<reference_existing_object>())
+        .def("__iter__", iterator<osmium::WayNodeList,return_internal_reference<>>())
+    ;
+    class_<osmium::RelationMember, boost::noncopyable>("RelationMember", no_init)
+        .add_property("ref", static_cast<osmium::object_id_type (osmium::RelationMember::*)() const>(&osmium::RelationMember::ref))
+        .add_property("type", &member_item_type)
+        .add_property("role", &osmium::RelationMember::role)
+    ;
+    class_<osmium::RelationMemberList, boost::noncopyable>("RelationMemberList", no_init)
+        .def("__len__", &osmium::RelationMemberList::size)
+        .def("__iter__", iterator<osmium::RelationMemberList,return_internal_reference<>>())
+    ;
+    class_<osmium::Changeset, boost::noncopyable>("Changeset", no_init)
+    ;
+    class_<osmium::OSMObject, boost::noncopyable>("OSMObject", no_init)
+        .add_property("id", &osmium::OSMObject::id)
+        .add_property("deleted", &osmium::OSMObject::deleted)
+        .add_property("visible", &osmium::OSMObject::visible)
+        .add_property("version", &osmium::OSMObject::version)
+        .add_property("changeset", &osmium::OSMObject::changeset)
+        .add_property("uid", &osmium::OSMObject::uid)
+        .def("user_is_anonymous", &osmium::OSMObject::user_is_anonymous)
+        .add_property("timestamp", &osmium::OSMObject::timestamp)
+        .add_property("user", &osmium::OSMObject::user)
+        .add_property("tags", make_function(&osmium::OSMObject::tags,
+                       return_value_policy<reference_existing_object>()))
+        .def("positive_id", &osmium::OSMObject::positive_id)
+    ;
+    class_<osmium::Node, bases<osmium::OSMObject>, boost::noncopyable>("Node", no_init)
+        .add_property("location", static_cast<osmium::Location (osmium::Node::*)() const>(&osmium::Node::location))
+    ;
+    class_<osmium::Way, bases<osmium::OSMObject>, boost::noncopyable>("Way", no_init)
+        .add_property("nodes", 
+                      make_function(static_cast<const osmium::WayNodeList& (osmium::Way::*)() const>(&osmium::Way::nodes),
+                      return_value_policy<reference_existing_object>()))
+        .def("is_closed", &osmium::Way::is_closed)
+        .def("ends_have_same_id", &osmium::Way::ends_have_same_id)
+        .def("ends_have_same_location", &osmium::Way::ends_have_same_location)
+    ;
+    class_<osmium::Relation, bases<osmium::OSMObject>, boost::noncopyable>("Relation", no_init)
+        .add_property("members", 
+                      make_function(static_cast<const osmium::RelationMemberList& (osmium::Relation::*)() const>(&osmium::Relation::members),
+                      return_value_policy<reference_existing_object>()))
+    ;
+    class_<osmium::Area, bases<osmium::OSMObject>, boost::noncopyable>("Area", no_init)
+    ;
+}
diff --git a/lib/osmium.cc b/lib/osmium.cc
new file mode 100644
index 0000000..d160dbe
--- /dev/null
+++ b/lib/osmium.cc
@@ -0,0 +1,86 @@
+#include <boost/python.hpp>
+
+#include <osmium/visitor.hpp>
+#include <osmium/index/map/all.hpp>
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/area/multipolygon_collector.hpp>
+#include <osmium/area/assembler.hpp>
+
+#include "generic_handler.hpp"
+
+template <typename T>
+void apply_reader_simple(osmium::io::Reader &rd, T &h) {
+    osmium::apply(rd, h);
+}
+
+
+template <typename T>
+void apply_reader_simple_with_location(osmium::io::Reader &rd,
+                         osmium::handler::NodeLocationsForWays<T> &l,
+                         BaseHandler &h) {
+    osmium::apply(rd, l, h);
+}
+
+PyObject *invalidLocationExceptionType = NULL;
+PyObject *notFoundExceptionType = NULL;
+
+void translator1(osmium::invalid_location const& x) {
+    PyErr_SetString(invalidLocationExceptionType, "Invalid location");
+}
+
+void translator2(osmium::not_found const& x) {
+    PyErr_SetString(notFoundExceptionType, "Element not found in index");
+}
+
+PyObject* createExceptionClass(const char* name, PyObject* baseTypeObj = PyExc_Exception)
+{
+    using std::string;
+    namespace bp = boost::python;
+
+    string scopeName = bp::extract<string>(bp::scope().attr("__name__"));
+    string qualifiedName0 = scopeName + "." + name;
+    char* qualifiedName1 = const_cast<char*>(qualifiedName0.c_str());
+
+    PyObject* typeObj = PyErr_NewException(qualifiedName1, baseTypeObj, 0);
+    if(!typeObj) bp::throw_error_already_set();
+    bp::scope().attr(name) = bp::handle<>(bp::borrowed(typeObj));
+    return typeObj;
+}
+
+#include "index.cc"
+
+BOOST_PYTHON_MODULE(_osmium)
+{
+    using namespace boost::python;
+
+    invalidLocationExceptionType = createExceptionClass("InvalidLocationError", PyExc_RuntimeError);
+    register_exception_translator<osmium::invalid_location>(&translator1);
+
+    notFoundExceptionType = createExceptionClass("NotFoundError", PyExc_KeyError);
+    register_exception_translator<osmium::not_found>(&translator2);
+
+    class_<osmium::handler::NodeLocationsForWays<LocationTable>, boost::noncopyable>("NodeLocationsForWays", 
+            init<LocationTable&>())
+        .def("ignore_errors", &osmium::handler::NodeLocationsForWays<LocationTable>::ignore_errors)
+    ;
+
+    class_<SimpleHandlerWrap, boost::noncopyable>("SimpleHandler")
+        .def("node", &BaseHandler::node, &SimpleHandlerWrap::default_node)
+        .def("way", &BaseHandler::way, &SimpleHandlerWrap::default_way)
+        .def("relation", &BaseHandler::relation, &SimpleHandlerWrap::default_relation)
+        .def("changeset", &BaseHandler::changeset, &SimpleHandlerWrap::default_changeset)
+        .def("area", &BaseHandler::area, &SimpleHandlerWrap::default_area)
+        .def("apply_file", &SimpleHandlerWrap::apply_file,
+              ("filename", arg("locations")=false, arg("idx")="sparse_mem_array"),
+             "Apply the handler to the given file. If locations is true, then\n"
+             "a location handler will be applied before, which saves the node\n"
+             "positions. In that case, the type of this position index can be\n"
+             "further selected in idx. If an area callback is implemented, then\n"
+             "the file will be scanned twice and a location handler and a\n"
+             "handler for assembling multipolygones and areas from ways will\n"
+             "be executed.")
+    ;
+    def("apply", &apply_reader_simple<BaseHandler>);
+    def("apply", &apply_reader_simple<osmium::handler::NodeLocationsForWays<LocationTable>>);
+    def("apply", &apply_reader_simple_with_location<LocationTable>);
+}
diff --git a/osmium/__init__.py b/osmium/__init__.py
new file mode 100644
index 0000000..f24c2d5
--- /dev/null
+++ b/osmium/__init__.py
@@ -0,0 +1,5 @@
+from ._osmium import *
+from . import _io as io
+from . import _osm as osm
+from . import _index as index
+from . import _geom as geom
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..b968e4c
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,57 @@
+from distutils.core import setup, Extension
+from os import environ
+from sys import version_info as pyversion, platform as osplatform
+from ctypes.util import find_library
+
+includes = []
+libs = []
+libdirs = []
+
+## boost dependencies
+includes.append('/usr/include')
+if osplatform in ["linux", "linux2"]:
+    libdirs.append('/usr/lib/x86_64-linux-gnu/')
+
+# try to find the boost library matching the python version
+suffixes = [ # Debian naming convention for version installed in parallel
+             "-py%d%d" % (pyversion.major, pyversion.minor),
+             # standard suffix for Python3
+             "%d" % (pyversion.major),
+             # standard naming
+             "",
+             # former naming schema?
+             "-mt"
+           ]
+for suf in suffixes:
+    lib = find_library("boost_python%s" % suf)
+    if lib is not None:
+        libs.append("boost_python%s" % suf)
+        break
+else:
+    raise Exception("Cannot find boost_python library")
+
+### osmium dependencies
+includes.append('../libosmium/include')
+osmium_libs = ('expat', 'pthread', 'z', 'protobuf-lite', 'osmpbf', 'bz2')
+libs.extend(osmium_libs)
+
+extensions = []
+extra_compile_args = [ '-std=c++11', '-D_LARGEFILE_SOURCE', '-D_FILE_OFFSET_BITS=64' ]
+
+for ext in ('osmium', 'io', 'osm', 'index', 'geom'):
+    extensions.append(Extension('osmium._%s' % ext,
+           sources = ['lib/%s.cc' % ext],
+           include_dirs = includes,
+           libraries = libs,
+           library_dirs = libdirs,
+           language = 'c++',
+           extra_compile_args = extra_compile_args
+         ))
+
+setup (name = 'pyosmium',
+       version = '0.1',
+       description = 'Provides python bindings for libosmium.',
+       packages = [ 'osmium' ],
+       ext_modules = extensions)
+
+
diff --git a/test/run_tests.py b/test/run_tests.py
new file mode 100644
index 0000000..2112450
--- /dev/null
+++ b/test/run_tests.py
@@ -0,0 +1,16 @@
+import sys
+import sysconfig
+import os
+
+# use some black magic to find the libraries in the build directory
+# borrowed from http://stackoverflow.com/questions/14320220/testing-python-c-libraries-get-build-path
+
+build_dir = "../../build/lib.%s-%d.%d" % (
+                    sysconfig.get_platform(),
+                    sys.version_info[0], sys.version_info[1])
+
+# insert after the current directory
+sys.path.insert(1, os.path.normpath(os.path.join(os.path.realpath(__file__), build_dir)))
+
+import nose
+nose.main()
diff --git a/test/test_helper.py b/test/test_helper.py
new file mode 100644
index 0000000..4c05172
--- /dev/null
+++ b/test/test_helper.py
@@ -0,0 +1,88 @@
+""" Provides some helper functions for test.
+"""
+import random
+import tempfile
+import os
+import osmium
+
+def _complete_object(o):
+    """Takes a hash with an incomplete OSM object description and returns a
+       complete one.
+    """
+    ret = { 'version' : '1', 'timestamp': "2012-05-01T15:06:20Z",
+            'changeset' : "11470653", 'uid' : "122294", 'user' : "foo",
+            'tags' : {}
+          }
+    ret.update(o)
+    if ret['type'] == 'N':
+        if 'lat' not in ret:
+            ret['lat'] = random.random()*180 - 90
+        if 'lon' not in ret:
+            ret['lon'] = random.random()*360 - 180
+    return ret
+
+def _write_osm_obj(fd, obj):
+    if obj['type'] == 'N':
+        fd.write(('<node id="%(id)d" lat="%(lat).8f" lon="%(lon).8f" version="%(version)s" timestamp="%(timestamp)s" changeset="%(changeset)s" uid="%(uid)s" user="%(user)s"'% obj).encode('utf-8'))
+        if obj['tags'] is None:
+            fd.write('/>\n'.encode('utf-8'))
+        else:
+            fd.write('>\n'.encode('utf-8'))
+            for k,v in iter(obj['tags'].items()):
+                fd.write(('  <tag k="%s" v="%s"/>\n' % (k, v)).encode('utf-8'))
+            fd.write('</node>\n'.encode('utf-8'))
+    elif obj['type'] == 'W':
+        fd.write(('<way id="%(id)d" version="%(version)s" changeset="%(changeset)s" timestamp="%(timestamp)s" user="%(user)s" uid="%(uid)s">\n' % obj).encode('utf-8'))
+        for nd in obj['nodes']:
+            fd.write(('<nd ref="%s" />\n' % (nd,)).encode('utf-8'))
+        for k,v in iter(obj['tags'].items()):
+            fd.write(('  <tag k="%s" v="%s"/>\n' % (k, v)).encode('utf-8'))
+        fd.write('</way>\n'.encode('utf-8'))
+    elif obj['type'] == 'R':
+        fd.write(('<relation id="%(id)d" version="%(version)s" changeset="%(changeset)s" timestamp="%(timestamp)s" user="%(user)s" uid="%(uid)s">\n' % obj).encode('utf-8'))
+        for mem in obj['members']:
+            fd.write(('  <member type="%s" ref="%s" role="%s"/>\n' % mem).encode('utf-8'))
+        for k,v in iter(obj['tags'].items()):
+            fd.write(('  <tag k="%s" v="%s"/>\n' % (k, v)).encode('utf-8'))
+        fd.write('</relation>\n'.encode('utf-8'))
+
+
+
+def create_osm_file(data):
+    """Creates a temporary osm XML file. The data is a list of OSM objects,
+       each described by a hash of attributes. Most attributes are optional
+       and will be filled with sensitive values, if missing. Mandatory are
+       only `type` and `id`. For ways, nodes are obligatory and for relations
+       the memberlist.
+    """
+    data.sort(key=lambda x:('NWR'.find(x['type']), x['id']))
+    with tempfile.NamedTemporaryFile(dir='/tmp', suffix='.osm', delete=False) as fd:
+        fname = fd.name
+        fd.write("<?xml version='1.0' encoding='UTF-8'?>\n".encode('utf-8'))
+        fd.write('<osm version="0.6" generator="test-pyosmium" timestamp="2014-08-26T20:22:02Z">\n'.encode('utf-8'))
+        fd.write('\t<bounds minlat="-90" minlon="-180" maxlat="90" maxlon="180"/>\n'.encode('utf-8'))
+
+        for obj in data:
+            _write_osm_obj(fd, _complete_object(obj))
+
+        fd.write('</osm>\n'.encode('utf-8'))
+
+    return fname
+
+def osmobj(kind, **args):
+    ret = dict(args)
+    ret['type'] = kind
+    return ret
+
+
+class HandlerTestBase:
+
+    def test_func(self):
+        fn = create_osm_file(self.data)
+        try:
+            rd = osmium.io.Reader(fn)
+            osmium.apply(rd, self.Handler())
+            rd.close()
+        finally:
+            os.remove(fn)
+
diff --git a/test/test_io.py b/test/test_io.py
new file mode 100644
index 0000000..a36e371
--- /dev/null
+++ b/test/test_io.py
@@ -0,0 +1,64 @@
+from nose.tools import *
+import unittest
+import os
+
+from test_helper import create_osm_file, osmobj
+
+import osmium as o
+
+class TestReaderFromFile(unittest.TestCase):
+
+    def _run_file(self, fn):
+        try:
+            rd = o.io.Reader(fn)
+            o.apply(rd, o.SimpleHandler())
+            rd.close()
+        finally:
+            os.remove(fn)
+
+    def test_node_only(self):
+        self._run_file(create_osm_file([osmobj('N', id=1)]))
+        
+    def test_way_only(self):
+        self._run_file(create_osm_file([osmobj('W', id=1, nodes=[1,2,3])]))
+
+    def test_relation_only(self):
+        self._run_file(create_osm_file([osmobj('R', id=1, members=[('W', 1, '')])]))
+
+    def test_node_with_tags(self):
+        self._run_file(create_osm_file([osmobj('N', id=1, 
+                                               tags=dict(foo='bar', name='xx'))]))
+        
+    def test_way_with_tags(self):
+        self._run_file(create_osm_file([osmobj('W', id=1, nodes=[1,2,3],
+                                               tags=dict(foo='bar', name='xx'))]))
+
+    def test_relation_with_tags(self):
+        self._run_file(create_osm_file([osmobj('R', id=1, members=[('W', 1, '')],
+                                               tags=dict(foo='bar', name='xx'))]))
+
+    def test_broken_timestamp(self):
+        fn = create_osm_file([osmobj('N', id=1, timestamp='x')])
+        try:
+            rd = o.io.Reader(fn)
+            with assert_raises(ValueError):
+                o.apply(rd, o.SimpleHandler())
+            rd.close()
+        finally:
+            os.remove(fn)
+
+
+class TestFileHeader(unittest.TestCase):
+
+    def test_file_header(self):
+        fn = create_osm_file([osmobj('N', id=1)])
+        try:
+            rd = o.io.Reader(fn)
+            h = rd.header()
+            assert_false(h.has_multiple_object_versions)
+            rd.close()
+        finally:
+            os.remove(fn)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/test/test_osm.py b/test/test_osm.py
new file mode 100644
index 0000000..ec3b3dd
--- /dev/null
+++ b/test/test_osm.py
@@ -0,0 +1,75 @@
+from nose.tools import *
+import unittest
+import os
+
+from test_helper import create_osm_file, osmobj, HandlerTestBase
+
+import osmium as o
+
+class TestLocation(unittest.TestCase):
+
+    def test_invalid_location(self):
+        loc = o.osm.Location()
+        assert_false(loc.valid())
+
+    def test_valid_location(self):
+        loc = o.osm.Location(1,10)
+        assert_equals(loc.lon, 1, 0.0001)
+        assert_equals(loc.lat, 10, 0.00001)
+        assert_equals(loc.x, 10000000)
+        assert_equals(loc.y, 100000000)
+
+class TestNodeAttributes(HandlerTestBase, unittest.TestCase):
+    data = [osmobj('N', id=1, version=5, changeset=58674, uid=42,
+                   timestamp='2014-01-31T06:23:35Z', user='anonymous')]
+
+    class Handler(o.SimpleHandler):
+        def node(self, n):
+            assert_equals(n.id, 1)
+            assert_equals(n.deleted, False)
+            assert_equals(n.visible, True)
+            assert_equals(n.version, 5)
+            assert_equals(n.changeset, 58674)
+            assert_equals(n.uid, 42)
+            assert_equals(n.user_is_anonymous(), False)
+            assert_equals(str(n.timestamp), '2014-01-31T06:23:35Z')
+            assert_equals(n.user, 'anonymous')
+            assert_equals(n.positive_id(), True)
+
+class TestWayAttributes(HandlerTestBase, unittest.TestCase):
+    data = [osmobj('W', id=1, version=5, changeset=58674, uid=42,
+                   timestamp='2014-01-31T06:23:35Z', user='anonymous',
+                   nodes = [1,2,3])]
+
+    class Handler(o.SimpleHandler):
+        def way(self, n):
+            assert_equals(n.id, 1)
+            assert_equals(n.deleted, False)
+            assert_equals(n.visible, True)
+            assert_equals(n.version, 5)
+            assert_equals(n.changeset, 58674)
+            assert_equals(n.uid, 42)
+            assert_equals(n.user_is_anonymous(), False)
+            assert_equals(str(n.timestamp), '2014-01-31T06:23:35Z')
+            assert_equals(n.user, 'anonymous')
+            assert_equals(n.positive_id(), True)
+
+class TestRelationAttributes(HandlerTestBase, unittest.TestCase):
+    data = [osmobj('R', id=1, version=5, changeset=58674, uid=42,
+                   timestamp='2014-01-31T06:23:35Z', user='anonymous',
+                   members=[('W',1,'')])]
+
+    class Handler(o.SimpleHandler):
+        def relation(self, n):
+            assert_equals(n.id, 1)
+            assert_equals(n.deleted, False)
+            assert_equals(n.visible, True)
+            assert_equals(n.version, 5)
+            assert_equals(n.changeset, 58674)
+            assert_equals(n.uid, 42)
+            assert_equals(n.user_is_anonymous(), False)
+            assert_equals(str(n.timestamp), '2014-01-31T06:23:35Z')
+            assert_equals(n.user, 'anonymous')
+            assert_equals(n.positive_id(), True)
+
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/pyosmium.git



More information about the Pkg-grass-devel mailing list