Skip to content

Commit

Permalink
Merge pull request #241 from lonvia/generic-apply-II
Browse files Browse the repository at this point in the history
Generalize osmium.apply() to work with an arbitrary number of handlers
  • Loading branch information
lonvia authored Mar 3, 2024
2 parents 49b7283 + 7fb887d commit 71262e2
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 143 deletions.
42 changes: 21 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ jobs:
runs-on: ubuntu-20.04

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Install packages
run: sudo apt-get install -y -qq libboost-dev libexpat1-dev zlib1g-dev libbz2-dev libproj-dev libgeos-dev liblz4-dev

- name: Set up Python 3.6
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.6

Expand All @@ -24,7 +24,7 @@ jobs:
shell: bash

- name: Set up Python 3.7
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.7

Expand All @@ -35,7 +35,7 @@ jobs:
shell: bash

- name: Set up Python 3.8
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.8

Expand All @@ -46,7 +46,7 @@ jobs:
shell: bash

- name: Set up Python 3.9
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.9

Expand All @@ -57,7 +57,7 @@ jobs:
shell: bash

- name: Set up Python 3.10
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.10"

Expand All @@ -68,7 +68,7 @@ jobs:
shell: bash

- name: Set up Python 3.11
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.11"

Expand All @@ -79,7 +79,7 @@ jobs:
shell: bash

- name: Set up Python 3.12
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.12"

Expand All @@ -105,10 +105,10 @@ jobs:
python-version: [3.6, 3.7, 3.8, 3.9, "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

Expand Down Expand Up @@ -167,13 +167,13 @@ jobs:
CXX: ${{ matrix.cxx }}

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: ./.github/actions/install-dependencies
with:
version: ${{ matrix.deps }}

- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: "${{ matrix.python }}"

Expand Down Expand Up @@ -212,7 +212,7 @@ jobs:
VCPKG_DEFAULT_BINARY_CACHE: C:/vcpkg_binary_cache

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: actions/cache@v3
with:
Expand All @@ -229,7 +229,7 @@ jobs:
shell: bash

- name: Set up Python 3.6
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.6

Expand All @@ -242,7 +242,7 @@ jobs:
CMAKE_TOOLCHAIN_FILE: C:/vcpkg/scripts/buildsystems/vcpkg.cmake

- name: Set up Python 3.7
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.7

Expand All @@ -255,7 +255,7 @@ jobs:
CMAKE_TOOLCHAIN_FILE: C:/vcpkg/scripts/buildsystems/vcpkg.cmake

- name: Set up Python 3.8
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.8

Expand All @@ -268,7 +268,7 @@ jobs:
CMAKE_TOOLCHAIN_FILE: C:/vcpkg/scripts/buildsystems/vcpkg.cmake

- name: Set up Python 3.9
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.9

Expand All @@ -281,7 +281,7 @@ jobs:
CMAKE_TOOLCHAIN_FILE: C:/vcpkg/scripts/buildsystems/vcpkg.cmake

- name: Set up Python 3.10
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.10"

Expand All @@ -294,7 +294,7 @@ jobs:
CMAKE_TOOLCHAIN_FILE: C:/vcpkg/scripts/buildsystems/vcpkg.cmake

- name: Set up Python 3.11
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.11"

Expand Down Expand Up @@ -325,10 +325,10 @@ jobs:
PYTEST_ADDOPTS: ${{ matrix.test-args }}

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ set_module_output(_osm osmium/osm)
pybind11_add_module(_osmium
lib/osmium.cc
lib/merge_input_reader.cc
lib/node_location_handler.cc
lib/simple_writer.cc
lib/write_handler.cc)
set_module_output(_osmium osmium)
Expand Down
86 changes: 3 additions & 83 deletions lib/base_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,114 +2,34 @@
*
* This file is part of pyosmium. (https://osmcode.org/pyosmium/)
*
* Copyright (C) 2023 Sarah Hoffmann <[email protected]> and others.
* Copyright (C) 2024 Sarah Hoffmann <[email protected]> and others.
* For a full list of authors see the git log.
*/
#ifndef PYOSMIUM_BASE_HANDLER_HPP
#define PYOSMIUM_BASE_HANDLER_HPP

#include <osmium/area/assembler.hpp>
#include <osmium/area/multipolygon_manager.hpp>
#include <osmium/handler.hpp>
#include <osmium/handler/node_locations_for_ways.hpp>
#include <osmium/index/map/all.hpp>

class BaseHandler : public osmium::handler::Handler
{
using IndexType =
osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>;
using IndexFactory =
osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>;
using MpManager =
osmium::area::MultipolygonManager<osmium::area::Assembler>;


protected:
enum pre_handler {
no_handler,
location_handler,
area_handler
};

public:
virtual ~BaseHandler() = default;
virtual osmium::osm_entity_bits::type enabled_callbacks() = 0;

// work around pybind's bad copy policy
// (see https://github.com/pybind/pybind11/issues/1241)
void node(const osmium::Node &o) { node(&o); }
void way(const osmium::Way &o) { way(&o); }
void way(osmium::Way &o) { way(&o); }
void relation(const osmium::Relation &o) { relation(&o); }
void changeset(const osmium::Changeset &o) { changeset(&o); }
void area(const osmium::Area &o) { area(&o); }

// actual handler functions
virtual void node(const osmium::Node*) {}
virtual void way(const osmium::Way*) {}
virtual void way(osmium::Way *) {}
virtual void relation(const osmium::Relation*) {}
virtual void changeset(const osmium::Changeset*) {}
virtual void area(const osmium::Area*) {}


private:
void apply_with_location(osmium::io::Reader &r, const std::string &idx)
{
const auto &map_factory = IndexFactory::instance();
auto index = map_factory.create_map(idx);
osmium::handler::NodeLocationsForWays<IndexType> location_handler(*index);
location_handler.ignore_errors();

osmium::apply(r, location_handler, *this);
}

void apply_with_area(osmium::io::Reader &r, MpManager &mp_manager,
const std::string &idx)
{
const auto &map_factory = IndexFactory::instance();
auto index = map_factory.create_map(idx);
osmium::handler::NodeLocationsForWays<IndexType> location_handler(*index);
location_handler.ignore_errors();

osmium::apply(r, location_handler, *this,
mp_manager.handler([this](const osmium::memory::Buffer &ab)
{ osmium::apply(ab, *this); }));
}

protected:
void apply(const osmium::io::File &file, osmium::osm_entity_bits::type types,
pre_handler pre = no_handler,
const std::string &idx = "flex_mem")
{
switch (pre) {
case no_handler:
{
osmium::io::Reader reader(file, types);
osmium::apply(reader, *this);
reader.close();
break;
}
case location_handler:
{
osmium::io::Reader reader(file, types);
apply_with_location(reader, idx);
reader.close();
break;
}
case area_handler:
{
osmium::area::Assembler::config_type assembler_config;
MpManager mp_manager{assembler_config};

osmium::relations::read_relations(file, mp_manager);

osmium::io::Reader reader2(file);
apply_with_area(reader2, mp_manager, idx);
reader2.close();
break;
}
}
}

};

#endif // PYOSMIUM_BASE_HANDLER_HPP
6 changes: 3 additions & 3 deletions lib/merge_input_reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
*
* This file is part of pyosmium. (https://osmcode.org/pyosmium/)
*
* Copyright (C) 2023 Sarah Hoffmann <[email protected]> and others.
* Copyright (C) 2024 Sarah Hoffmann <[email protected]> and others.
* For a full list of authors see the git log.
*/
#include <pybind11/pybind11.h>
Expand Down Expand Up @@ -133,7 +133,7 @@ class MergeInputReader
objects.sort(osmium::object_order_type_id_reverse_version());
osmium::item_type prev_type = osmium::item_type::undefined;
osmium::object_id_type prev_id = 0;
for (const auto &item: objects) {
for (auto &item: objects) {
if (item.type() != prev_type || item.id() != prev_id) {
prev_type = item.type();
prev_id = item.id();
Expand All @@ -142,7 +142,7 @@ class MergeInputReader
}
} else {
objects.sort(osmium::object_order_type_id_version());
osmium::apply(objects.cbegin(), objects.cend(), handler);
osmium::apply(objects.begin(), objects.end(), handler);
}

objects = osmium::ObjectPointerCollection();
Expand Down
65 changes: 65 additions & 0 deletions lib/node_location_handler.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: BSD-2-Clause
*
* This file is part of pyosmium. (https://osmcode.org/pyosmium/)
*
* Copyright (C) 2024 Sarah Hoffmann <[email protected]> and others.
* For a full list of authors see the git log.
*/
#include <pybind11/pybind11.h>

#include <osmium/index/map/all.hpp>
#include <osmium/handler/node_locations_for_ways.hpp>

#include "base_handler.h"

using LocationTable =
osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>;
using NodeLocationHandler =
osmium::handler::NodeLocationsForWays<LocationTable>;


class NodeLocationsForWays : public BaseHandler
{
public:
NodeLocationsForWays(LocationTable &idx)
: handler(idx)
{}

void node(const osmium::Node *o) override
{
handler.node(*o);
}

void way(osmium::Way *o) override
{
if (apply_nodes_to_ways) {
handler.way(*o);
}
}

bool get_apply_nodes_to_ways() const { return apply_nodes_to_ways; }

void set_apply_nodes_to_ways(bool val) { apply_nodes_to_ways = val; }

void ignore_errors() { handler.ignore_errors(); }

private:
NodeLocationHandler handler;
bool apply_nodes_to_ways = true;
};

namespace py = pybind11;

void init_node_location_handler(py::module &m)
{
py::class_<NodeLocationsForWays, BaseHandler>(m, "NodeLocationsForWays")
.def(py::init<LocationTable&>(), py::keep_alive<1, 2>())
.def("ignore_errors", &NodeLocationsForWays::ignore_errors)
.def_property("apply_nodes_to_ways",
&NodeLocationsForWays::get_apply_nodes_to_ways,
&NodeLocationsForWays::set_apply_nodes_to_ways,
"When set to false, locations are only collected "
"and not automatically applied to way nodes.")
;

}
Loading

0 comments on commit 71262e2

Please sign in to comment.