Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalize osmium.apply() to work with an arbitrary number of handlers #241

Merged
merged 5 commits into from
Mar 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading