Merge commit '6eb4f090f98f6b17a23c57768c16b7716b6c9cbd' as 'third_party/libosmium'
This commit is contained in:
+477
@@ -0,0 +1,477 @@
|
||||
#ifndef GDALCPP_HPP
|
||||
#define GDALCPP_HPP
|
||||
|
||||
/*
|
||||
|
||||
C++11 wrapper classes for GDAL/OGR.
|
||||
|
||||
Version 1.1.1
|
||||
|
||||
https://github.com/joto/gdalcpp
|
||||
|
||||
Copyright 2015 Jochen Topf <jochen@topf.org>
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <gdal_priv.h>
|
||||
#include <gdal_version.h>
|
||||
#include <ogr_api.h>
|
||||
#include <ogrsf_frmts.h>
|
||||
|
||||
namespace gdalcpp {
|
||||
|
||||
#if GDAL_VERSION_MAJOR >= 2
|
||||
using gdal_driver_type = GDALDriver;
|
||||
using gdal_dataset_type = GDALDataset;
|
||||
#else
|
||||
using gdal_driver_type = OGRSFDriver;
|
||||
using gdal_dataset_type = OGRDataSource;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Exception thrown for all errors in this class.
|
||||
*/
|
||||
class gdal_error : public std::runtime_error {
|
||||
|
||||
std::string m_driver;
|
||||
std::string m_dataset;
|
||||
std::string m_layer;
|
||||
std::string m_field;
|
||||
OGRErr m_error;
|
||||
|
||||
public:
|
||||
|
||||
gdal_error(const std::string& message,
|
||||
OGRErr error,
|
||||
const std::string& driver = "",
|
||||
const std::string& dataset = "",
|
||||
const std::string& layer = "",
|
||||
const std::string& field = "") :
|
||||
std::runtime_error(message),
|
||||
m_driver(driver),
|
||||
m_dataset(dataset),
|
||||
m_layer(layer),
|
||||
m_field(field),
|
||||
m_error(error) {
|
||||
}
|
||||
|
||||
const std::string& driver() const {
|
||||
return m_driver;
|
||||
}
|
||||
|
||||
const std::string& dataset() const {
|
||||
return m_dataset;
|
||||
}
|
||||
|
||||
const std::string& layer() const {
|
||||
return m_layer;
|
||||
}
|
||||
|
||||
const std::string& field() const {
|
||||
return m_field;
|
||||
}
|
||||
|
||||
OGRErr error() const {
|
||||
return m_error;
|
||||
}
|
||||
|
||||
}; // class gdal_error
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct init_wrapper {
|
||||
#if GDAL_VERSION_MAJOR >= 2
|
||||
init_wrapper() { GDALAllRegister(); }
|
||||
#else
|
||||
init_wrapper() { OGRRegisterAll(); }
|
||||
~init_wrapper() { OGRCleanupAll(); }
|
||||
#endif
|
||||
};
|
||||
|
||||
struct init_library {
|
||||
init_library() {
|
||||
static init_wrapper iw;
|
||||
}
|
||||
};
|
||||
|
||||
class Driver : private init_library {
|
||||
|
||||
gdal_driver_type* m_driver;
|
||||
|
||||
public:
|
||||
|
||||
Driver(const std::string& driver_name) :
|
||||
init_library(),
|
||||
#if GDAL_VERSION_MAJOR >= 2
|
||||
m_driver(GetGDALDriverManager()->GetDriverByName(driver_name.c_str())) {
|
||||
#else
|
||||
m_driver(OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str())) {
|
||||
#endif
|
||||
if (!m_driver) {
|
||||
throw gdal_error(std::string("unknown driver: '") + driver_name + "'", OGRERR_NONE, driver_name);
|
||||
}
|
||||
}
|
||||
|
||||
gdal_driver_type& get() const {
|
||||
return *m_driver;
|
||||
}
|
||||
|
||||
}; // struct Driver
|
||||
|
||||
struct Options {
|
||||
|
||||
std::vector<std::string> m_options;
|
||||
std::unique_ptr<const char*[]> m_ptrs;
|
||||
|
||||
Options(const std::vector<std::string>& options) :
|
||||
m_options(options),
|
||||
m_ptrs(new const char*[options.size()+1]) {
|
||||
std::transform(m_options.begin(), m_options.end(), m_ptrs.get(), [&](const std::string& s) {
|
||||
return s.data();
|
||||
});
|
||||
m_ptrs[options.size()] = nullptr;
|
||||
}
|
||||
|
||||
char** get() const {
|
||||
return const_cast<char**>(m_ptrs.get());
|
||||
}
|
||||
|
||||
}; // struct Options
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class SRS {
|
||||
|
||||
OGRSpatialReference m_spatial_reference;
|
||||
|
||||
public:
|
||||
|
||||
SRS() :
|
||||
m_spatial_reference() {
|
||||
auto result = m_spatial_reference.SetWellKnownGeogCS("WGS84");
|
||||
if (result != OGRERR_NONE) {
|
||||
throw gdal_error(std::string("can not initialize spatial reference system WGS84"), result);
|
||||
}
|
||||
}
|
||||
|
||||
explicit SRS(int epsg) :
|
||||
m_spatial_reference() {
|
||||
auto result = m_spatial_reference.importFromEPSG(epsg);
|
||||
if (result != OGRERR_NONE) {
|
||||
throw gdal_error(std::string("can not initialize spatial reference system for EPSG:") + std::to_string(epsg), result);
|
||||
}
|
||||
}
|
||||
|
||||
explicit SRS(const char* name) :
|
||||
m_spatial_reference() {
|
||||
auto result = m_spatial_reference.importFromProj4(name);
|
||||
if (result != OGRERR_NONE) {
|
||||
throw gdal_error(std::string("can not initialize spatial reference system '") + name + "'", result);
|
||||
}
|
||||
}
|
||||
|
||||
explicit SRS(const std::string& name) :
|
||||
m_spatial_reference() {
|
||||
auto result = m_spatial_reference.importFromProj4(name.c_str());
|
||||
if (result != OGRERR_NONE) {
|
||||
throw gdal_error(std::string("can not initialize spatial reference system '") + name + "'", result);
|
||||
}
|
||||
}
|
||||
|
||||
explicit SRS(const OGRSpatialReference& spatial_reference) :
|
||||
m_spatial_reference(spatial_reference) {
|
||||
}
|
||||
|
||||
OGRSpatialReference& get() {
|
||||
return m_spatial_reference;
|
||||
}
|
||||
|
||||
const OGRSpatialReference& get() const {
|
||||
return m_spatial_reference;
|
||||
}
|
||||
|
||||
}; // class SRS
|
||||
|
||||
class Dataset {
|
||||
|
||||
struct gdal_dataset_deleter {
|
||||
|
||||
void operator()(gdal_dataset_type* ds) {
|
||||
#if GDAL_VERSION_MAJOR >= 2
|
||||
GDALClose(ds);
|
||||
#else
|
||||
OGRDataSource::DestroyDataSource(ds);
|
||||
#endif
|
||||
}
|
||||
|
||||
}; // struct gdal_dataset_deleter
|
||||
|
||||
std::string m_driver_name;
|
||||
std::string m_dataset_name;
|
||||
detail::Options m_options;
|
||||
SRS m_srs;
|
||||
std::unique_ptr<gdal_dataset_type, gdal_dataset_deleter> m_dataset;
|
||||
uint64_t m_edit_count = 0;
|
||||
uint64_t m_max_edit_count = 0;
|
||||
|
||||
public:
|
||||
|
||||
Dataset(const std::string& driver_name, const std::string& dataset_name, const SRS& srs = SRS{}, const std::vector<std::string>& options = {}) :
|
||||
m_driver_name(driver_name),
|
||||
m_dataset_name(dataset_name),
|
||||
m_options(options),
|
||||
m_srs(srs),
|
||||
#if GDAL_VERSION_MAJOR >= 2
|
||||
m_dataset(detail::Driver(driver_name).get().Create(dataset_name.c_str(), 0, 0, 0, GDT_Unknown, m_options.get())) {
|
||||
#else
|
||||
m_dataset(detail::Driver(driver_name).get().CreateDataSource(dataset_name.c_str(), m_options.get())) {
|
||||
#endif
|
||||
if (!m_dataset) {
|
||||
throw gdal_error(std::string("failed to create dataset '") + dataset_name + "'", OGRERR_NONE, driver_name, dataset_name);
|
||||
}
|
||||
}
|
||||
|
||||
~Dataset() {
|
||||
try {
|
||||
if (m_edit_count > 0) {
|
||||
commit_transaction();
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& driver_name() const {
|
||||
return m_driver_name;
|
||||
}
|
||||
|
||||
const std::string& dataset_name() const {
|
||||
return m_dataset_name;
|
||||
}
|
||||
|
||||
gdal_dataset_type& get() const {
|
||||
return *m_dataset;
|
||||
}
|
||||
|
||||
SRS& srs() {
|
||||
return m_srs;
|
||||
}
|
||||
|
||||
void exec(const char* sql) {
|
||||
auto result = m_dataset->ExecuteSQL(sql, nullptr, nullptr);
|
||||
if (result) {
|
||||
m_dataset->ReleaseResultSet(result);
|
||||
}
|
||||
}
|
||||
|
||||
void exec(const std::string& sql) {
|
||||
exec(sql.c_str());
|
||||
}
|
||||
|
||||
Dataset& start_transaction() {
|
||||
#if GDAL_VERSION_MAJOR >= 2
|
||||
m_dataset->StartTransaction();
|
||||
#else
|
||||
OGRLayer* layer = m_dataset->GetLayer(0);
|
||||
if (layer) {
|
||||
layer->StartTransaction();
|
||||
}
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dataset& commit_transaction() {
|
||||
#if GDAL_VERSION_MAJOR >= 2
|
||||
m_dataset->CommitTransaction();
|
||||
#else
|
||||
OGRLayer* layer = m_dataset->GetLayer(0);
|
||||
if (layer) {
|
||||
layer->CommitTransaction();
|
||||
}
|
||||
#endif
|
||||
m_edit_count = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void prepare_edit() {
|
||||
if (m_max_edit_count != 0 && m_edit_count == 0) {
|
||||
start_transaction();
|
||||
}
|
||||
}
|
||||
|
||||
void finalize_edit() {
|
||||
if (m_max_edit_count != 0 && ++m_edit_count > m_max_edit_count) {
|
||||
commit_transaction();
|
||||
}
|
||||
}
|
||||
|
||||
Dataset& enable_auto_transactions(uint64_t edits = 100000) {
|
||||
m_max_edit_count = edits;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dataset& disable_auto_transactions() {
|
||||
if (m_max_edit_count != 0 && m_edit_count > 0) {
|
||||
commit_transaction();
|
||||
}
|
||||
m_max_edit_count = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
}; // class Dataset
|
||||
|
||||
class Layer {
|
||||
|
||||
detail::Options m_options;
|
||||
Dataset& m_dataset;
|
||||
OGRLayer* m_layer;
|
||||
|
||||
public:
|
||||
|
||||
Layer(Dataset& dataset, const std::string& layer_name, OGRwkbGeometryType type, const std::vector<std::string>& options = {}) :
|
||||
m_options(options),
|
||||
m_dataset(dataset),
|
||||
m_layer(dataset.get().CreateLayer(layer_name.c_str(), &dataset.srs().get(), type, m_options.get())) {
|
||||
if (!m_layer) {
|
||||
throw gdal_error(std::string("failed to create layer '") + layer_name + "'", OGRERR_NONE,
|
||||
dataset.driver_name(), dataset.dataset_name(), layer_name);
|
||||
}
|
||||
}
|
||||
|
||||
OGRLayer& get() {
|
||||
return *m_layer;
|
||||
}
|
||||
|
||||
const OGRLayer& get() const {
|
||||
return *m_layer;
|
||||
}
|
||||
|
||||
Dataset& dataset() const {
|
||||
return m_dataset;
|
||||
}
|
||||
|
||||
const char* name() const {
|
||||
return m_layer->GetName();
|
||||
}
|
||||
|
||||
Layer& add_field(const std::string& field_name, OGRFieldType type, int width, int precision=0) {
|
||||
OGRFieldDefn field(field_name.c_str(), type);
|
||||
field.SetWidth(width);
|
||||
field.SetPrecision(precision);
|
||||
|
||||
if (m_layer->CreateField(&field) != OGRERR_NONE) {
|
||||
throw gdal_error(std::string("failed to create field '") + field_name + "' in layer '" + name() + "'", OGRERR_NONE,
|
||||
m_dataset.driver_name(), m_dataset.dataset_name(), name(), field_name);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void create_feature(OGRFeature* feature) {
|
||||
dataset().prepare_edit();
|
||||
OGRErr result = m_layer->CreateFeature(feature);
|
||||
if (result != OGRERR_NONE) {
|
||||
throw gdal_error(std::string("creating feature in layer '") + name() + "' failed", result, dataset().driver_name(), dataset().dataset_name());
|
||||
}
|
||||
dataset().finalize_edit();
|
||||
}
|
||||
|
||||
Layer& start_transaction() {
|
||||
#if GDAL_VERSION_MAJOR < 2
|
||||
OGRErr result = m_layer->StartTransaction();
|
||||
if (result != OGRERR_NONE) {
|
||||
throw gdal_error(std::string("starting transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name());
|
||||
}
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
Layer& commit_transaction() {
|
||||
#if GDAL_VERSION_MAJOR < 2
|
||||
OGRErr result = m_layer->CommitTransaction();
|
||||
if (result != OGRERR_NONE) {
|
||||
throw gdal_error(std::string("committing transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name());
|
||||
}
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
}; // class Layer
|
||||
|
||||
class Feature {
|
||||
|
||||
struct ogr_feature_deleter {
|
||||
|
||||
void operator()(OGRFeature* feature) {
|
||||
OGRFeature::DestroyFeature(feature);
|
||||
}
|
||||
|
||||
}; // struct ogr_feature_deleter
|
||||
|
||||
Layer& m_layer;
|
||||
std::unique_ptr<OGRFeature, ogr_feature_deleter> m_feature;
|
||||
|
||||
public:
|
||||
|
||||
Feature(Layer& layer, std::unique_ptr<OGRGeometry>&& geometry) :
|
||||
m_layer(layer),
|
||||
m_feature(OGRFeature::CreateFeature(m_layer.get().GetLayerDefn())) {
|
||||
if (!m_feature) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
OGRErr result = m_feature->SetGeometryDirectly(geometry.release());
|
||||
if (result != OGRERR_NONE) {
|
||||
throw gdal_error(std::string("setting feature geometry in layer '") + m_layer.name() + "' failed", result, m_layer.dataset().driver_name(), m_layer.dataset().dataset_name());
|
||||
}
|
||||
}
|
||||
|
||||
void add_to_layer() {
|
||||
m_layer.create_feature(m_feature.get());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Feature& set_field(int n, T&& arg) {
|
||||
m_feature->SetField(n, std::forward<T>(arg));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Feature& set_field(const char* name, T&& arg) {
|
||||
m_feature->SetField(name, std::forward<T>(arg));
|
||||
return *this;
|
||||
}
|
||||
|
||||
}; // class Feature
|
||||
|
||||
} // namespace gdalcpp
|
||||
|
||||
#endif // GDALCPP_HPP
|
||||
@@ -0,0 +1,234 @@
|
||||
#ifndef OSMIUM_AREA_ASSEMBLER_HPP
|
||||
#define OSMIUM_AREA_ASSEMBLER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/area/assembler_config.hpp>
|
||||
#include <osmium/area/detail/basic_assembler_with_tags.hpp>
|
||||
#include <osmium/area/detail/segment_list.hpp>
|
||||
#include <osmium/area/problem_reporter.hpp>
|
||||
#include <osmium/area/stats.hpp>
|
||||
#include <osmium/builder/osm_object_builder.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace area {
|
||||
|
||||
/**
|
||||
* Assembles area objects from closed ways or multipolygon relations
|
||||
* and their members.
|
||||
*/
|
||||
class Assembler : public detail::BasicAssemblerWithTags {
|
||||
|
||||
bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Way& way) {
|
||||
osmium::builder::AreaBuilder builder{out_buffer};
|
||||
builder.initialize_from_object(way);
|
||||
|
||||
const bool area_okay = create_rings();
|
||||
if (area_okay || config().create_empty_areas) {
|
||||
builder.add_item(way.tags());
|
||||
}
|
||||
if (area_okay) {
|
||||
add_rings_to_area(builder);
|
||||
}
|
||||
|
||||
if (report_ways()) {
|
||||
config().problem_reporter->report_way(way);
|
||||
}
|
||||
|
||||
return area_okay || config().create_empty_areas;
|
||||
}
|
||||
|
||||
bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) {
|
||||
set_num_members(members.size());
|
||||
osmium::builder::AreaBuilder builder{out_buffer};
|
||||
builder.initialize_from_object(relation);
|
||||
|
||||
const bool area_okay = create_rings();
|
||||
if (area_okay || config().create_empty_areas) {
|
||||
if (config().keep_type_tag) {
|
||||
builder.add_item(relation.tags());
|
||||
} else {
|
||||
copy_tags_without_type(builder, relation.tags());
|
||||
}
|
||||
}
|
||||
if (area_okay) {
|
||||
add_rings_to_area(builder);
|
||||
}
|
||||
|
||||
if (report_ways()) {
|
||||
for (const osmium::Way* way : members) {
|
||||
config().problem_reporter->report_way(*way);
|
||||
}
|
||||
}
|
||||
|
||||
return area_okay || config().create_empty_areas;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit Assembler(const config_type& config) :
|
||||
detail::BasicAssemblerWithTags(config) {
|
||||
}
|
||||
|
||||
~Assembler() noexcept = default;
|
||||
|
||||
/**
|
||||
* Assemble an area from the given way.
|
||||
* The resulting area is put into the out_buffer.
|
||||
*
|
||||
* @returns false if there was some kind of error building the
|
||||
* area, true otherwise.
|
||||
*/
|
||||
bool operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) {
|
||||
if (!config().create_way_polygons) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (config().problem_reporter) {
|
||||
config().problem_reporter->set_object(osmium::item_type::way, way.id());
|
||||
config().problem_reporter->set_nodes(way.nodes().size());
|
||||
}
|
||||
|
||||
// Ignore (but count) ways without segments.
|
||||
if (way.nodes().size() < 2) {
|
||||
++stats().short_ways;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!way.ends_have_same_id()) {
|
||||
++stats().duplicate_nodes;
|
||||
if (config().problem_reporter) {
|
||||
config().problem_reporter->report_duplicate_node(way.nodes().front().ref(), way.nodes().back().ref(), way.nodes().front().location());
|
||||
}
|
||||
}
|
||||
|
||||
++stats().from_ways;
|
||||
stats().invalid_locations = segment_list().extract_segments_from_way(config().problem_reporter,
|
||||
stats().duplicate_nodes,
|
||||
way);
|
||||
if (!config().ignore_invalid_locations && stats().invalid_locations > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config().debug_level > 0) {
|
||||
std::cerr << "\nAssembling way " << way.id() << " containing " << segment_list().size() << " nodes\n";
|
||||
}
|
||||
|
||||
// Now create the Area object and add the attributes and tags
|
||||
// from the way.
|
||||
const bool okay = create_area(out_buffer, way);
|
||||
if (okay) {
|
||||
out_buffer.commit();
|
||||
} else {
|
||||
out_buffer.rollback();
|
||||
}
|
||||
|
||||
if (debug()) {
|
||||
std::cerr << "Done: " << stats() << "\n";
|
||||
}
|
||||
|
||||
return okay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble an area from the given relation and its members.
|
||||
* The resulting area is put into the out_buffer.
|
||||
*
|
||||
* @returns false if there was some kind of error building the
|
||||
* area(s), true otherwise.
|
||||
*/
|
||||
bool operator()(const osmium::Relation& relation, const std::vector<const osmium::Way*>& members, osmium::memory::Buffer& out_buffer) {
|
||||
if (!config().create_new_style_polygons) {
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(relation.cmembers().size() >= members.size());
|
||||
|
||||
if (config().problem_reporter) {
|
||||
config().problem_reporter->set_object(osmium::item_type::relation, relation.id());
|
||||
}
|
||||
|
||||
if (relation.members().empty()) {
|
||||
++stats().no_way_in_mp_relation;
|
||||
return false;
|
||||
}
|
||||
|
||||
++stats().from_relations;
|
||||
stats().invalid_locations = segment_list().extract_segments_from_ways(config().problem_reporter,
|
||||
stats().duplicate_nodes,
|
||||
stats().duplicate_ways,
|
||||
relation,
|
||||
members);
|
||||
if (!config().ignore_invalid_locations && stats().invalid_locations > 0) {
|
||||
return false;
|
||||
}
|
||||
stats().member_ways = members.size();
|
||||
|
||||
if (stats().member_ways == 1) {
|
||||
++stats().single_way_in_mp_relation;
|
||||
}
|
||||
|
||||
if (config().debug_level > 0) {
|
||||
std::cerr << "\nAssembling relation " << relation.id() << " containing " << members.size() << " way members with " << segment_list().size() << " nodes\n";
|
||||
}
|
||||
|
||||
// Now create the Area object and add the attributes and tags
|
||||
// from the relation.
|
||||
bool okay = create_area(out_buffer, relation, members);
|
||||
if (okay) {
|
||||
out_buffer.commit();
|
||||
} else {
|
||||
out_buffer.rollback();
|
||||
}
|
||||
|
||||
return okay;
|
||||
}
|
||||
|
||||
}; // class Assembler
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_ASSEMBLER_HPP
|
||||
@@ -0,0 +1,154 @@
|
||||
#ifndef OSMIUM_AREA_ASSEMBLER_CONFIG_HPP
|
||||
#define OSMIUM_AREA_ASSEMBLER_CONFIG_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/util/compatibility.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace area {
|
||||
|
||||
class ProblemReporter;
|
||||
|
||||
/**
|
||||
* Configuration for osmium::area::Assembler objects. Create this
|
||||
* once, set the options you want and then re-use it every time you
|
||||
* create an Assembler object.
|
||||
*/
|
||||
struct AssemblerConfig {
|
||||
|
||||
/**
|
||||
* Optional pointer to problem reporter.
|
||||
*/
|
||||
ProblemReporter* problem_reporter = nullptr;
|
||||
|
||||
/**
|
||||
* Debug level. If this is greater than zero, debug messages will
|
||||
* be printed to stderr. Available levels are 1 to 3. Note that
|
||||
* level 2 and above will generate a lot of messages!
|
||||
*/
|
||||
int debug_level = 0;
|
||||
|
||||
/**
|
||||
* The roles of multipolygon members are ignored when assembling
|
||||
* multipolygons, because they are often missing or wrong. If this
|
||||
* is set, the roles are checked after the multipolygons are built
|
||||
* against what the assembly process decided where the inner and
|
||||
* outer rings are. This slows down the processing, so it only
|
||||
* makes sense if you want to get the problem reports.
|
||||
*/
|
||||
bool check_roles = false;
|
||||
|
||||
/**
|
||||
* When the assembler can't create an area, usually because its
|
||||
* geometry would be invalid, it will create an "empty" area object
|
||||
* without rings. This allows you to detect where an area was
|
||||
* invalid.
|
||||
*
|
||||
* If this is set to false, invalid areas will simply be discarded.
|
||||
*/
|
||||
bool create_empty_areas = true;
|
||||
|
||||
/**
|
||||
* Create areas for (multi)polygons where the tags are on the
|
||||
* relation.
|
||||
*
|
||||
* If this is set to false, those areas will simply be discarded.
|
||||
*/
|
||||
bool create_new_style_polygons = true;
|
||||
|
||||
/**
|
||||
* Create areas for (multi)polygons where the tags are on the
|
||||
* outer way(s). This is ignored by the area::Assembler which
|
||||
* doesn't support old-style multipolygons any more. Use the
|
||||
* area::AssemblerLegacy if you need this.
|
||||
*
|
||||
* If this is set to false, those areas will simply be discarded.
|
||||
*/
|
||||
bool create_old_style_polygons = true;
|
||||
|
||||
/**
|
||||
* Create areas for polygons created from ways.
|
||||
*
|
||||
* If this is set to false, those areas will simply be discarded.
|
||||
*/
|
||||
bool create_way_polygons = true;
|
||||
|
||||
/**
|
||||
* Keep the type tag from multipolygon relations on the area
|
||||
* object. By default this is false, and the type tag will be
|
||||
* removed.
|
||||
*/
|
||||
bool keep_type_tag = false;
|
||||
|
||||
/**
|
||||
* If there is an invalid location in any of the ways needed for
|
||||
* assembling the multipolygon, the assembler will normally fail.
|
||||
* If this is set, the assembler will silently ignore the invalid
|
||||
* locations pretending them to be not referenced from the ways.
|
||||
* This will allow some areas to be built, others will now be
|
||||
* incorrect. This can sometimes be useful to assemble areas
|
||||
* crossing the boundary of an extract, but you will also get
|
||||
* geometrically valid but wrong (multi)polygons.
|
||||
*/
|
||||
bool ignore_invalid_locations = false;
|
||||
|
||||
AssemblerConfig() noexcept = default;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @deprecated Use default constructor and set values afterwards.
|
||||
*/
|
||||
explicit AssemblerConfig(ProblemReporter* pr, bool d = false) :
|
||||
problem_reporter(pr),
|
||||
debug_level(d) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable debug output to stderr. This is for Osmium
|
||||
* developers only.
|
||||
*
|
||||
* @deprecated Set debug_level directly.
|
||||
*/
|
||||
OSMIUM_DEPRECATED void enable_debug_output(bool d = true) {
|
||||
debug_level = d;
|
||||
}
|
||||
|
||||
}; // struct AssemblerConfig
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_ASSEMBLER_CONFIG_HPP
|
||||
@@ -0,0 +1,382 @@
|
||||
#ifndef OSMIUM_AREA_ASSEMBLER_LEGACY_HPP
|
||||
#define OSMIUM_AREA_ASSEMBLER_LEGACY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/area/assembler_config.hpp>
|
||||
#include <osmium/area/detail/basic_assembler_with_tags.hpp>
|
||||
#include <osmium/area/detail/proto_ring.hpp>
|
||||
#include <osmium/area/detail/segment_list.hpp>
|
||||
#include <osmium/area/problem_reporter.hpp>
|
||||
#include <osmium/area/stats.hpp>
|
||||
#include <osmium/builder/osm_object_builder.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/memory/collection.hpp>
|
||||
#include <osmium/osm/area.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/tags/filter.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace area {
|
||||
|
||||
/**
|
||||
* Assembles area objects from closed ways or multipolygon relations
|
||||
* and their members.
|
||||
*/
|
||||
class AssemblerLegacy : public detail::BasicAssemblerWithTags {
|
||||
|
||||
void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Way& way) const {
|
||||
builder.add_item(way.tags());
|
||||
}
|
||||
|
||||
void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set<const osmium::Way*>& ways) const {
|
||||
std::map<std::string, std::size_t> counter;
|
||||
for (const osmium::Way* way : ways) {
|
||||
for (const auto& tag : way->tags()) {
|
||||
std::string kv{tag.key()};
|
||||
kv.append(1, '\0');
|
||||
kv.append(tag.value());
|
||||
++counter[kv];
|
||||
}
|
||||
}
|
||||
|
||||
const std::size_t num_ways = ways.size();
|
||||
for (const auto& t_c : counter) {
|
||||
if (debug()) {
|
||||
std::cerr << " tag " << t_c.first << " is used " << t_c.second << " times in " << num_ways << " ways\n";
|
||||
}
|
||||
if (t_c.second == num_ways) {
|
||||
const std::size_t len = std::strlen(t_c.first.c_str());
|
||||
tl_builder.add_tag(t_c.first.c_str(), t_c.first.c_str() + len + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MPFilter : public osmium::tags::KeyFilter {
|
||||
|
||||
MPFilter() : osmium::tags::KeyFilter(true) {
|
||||
add(false, "type");
|
||||
add(false, "created_by");
|
||||
add(false, "source");
|
||||
add(false, "note");
|
||||
add(false, "test:id");
|
||||
add(false, "test:section");
|
||||
}
|
||||
|
||||
}; // struct MPFilter
|
||||
|
||||
static const MPFilter& filter() noexcept {
|
||||
static const MPFilter filter;
|
||||
return filter;
|
||||
}
|
||||
|
||||
void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Relation& relation) {
|
||||
const auto count = std::count_if(relation.tags().cbegin(), relation.tags().cend(), std::cref(filter()));
|
||||
|
||||
if (debug()) {
|
||||
std::cerr << " found " << count << " tags on relation (without ignored ones)\n";
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
if (debug()) {
|
||||
std::cerr << " use tags from relation\n";
|
||||
}
|
||||
|
||||
if (config().keep_type_tag) {
|
||||
builder.add_item(relation.tags());
|
||||
} else {
|
||||
copy_tags_without_type(builder, relation.tags());
|
||||
}
|
||||
} else {
|
||||
++stats().no_tags_on_relation;
|
||||
if (debug()) {
|
||||
std::cerr << " use tags from outer ways\n";
|
||||
}
|
||||
std::set<const osmium::Way*> ways;
|
||||
for (const auto& ring : rings()) {
|
||||
if (ring.is_outer()) {
|
||||
ring.get_ways(ways);
|
||||
}
|
||||
}
|
||||
if (ways.size() == 1) {
|
||||
if (debug()) {
|
||||
std::cerr << " only one outer way\n";
|
||||
}
|
||||
builder.add_item((*ways.cbegin())->tags());
|
||||
} else {
|
||||
if (debug()) {
|
||||
std::cerr << " multiple outer ways, get common tags\n";
|
||||
}
|
||||
osmium::builder::TagListBuilder tl_builder{builder};
|
||||
add_common_tags(tl_builder, ways);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Way& way) {
|
||||
osmium::builder::AreaBuilder builder{out_buffer};
|
||||
builder.initialize_from_object(way);
|
||||
|
||||
const bool area_okay = create_rings();
|
||||
if (area_okay || config().create_empty_areas) {
|
||||
add_tags_to_area(builder, way);
|
||||
}
|
||||
if (area_okay) {
|
||||
add_rings_to_area(builder);
|
||||
}
|
||||
|
||||
if (report_ways()) {
|
||||
config().problem_reporter->report_way(way);
|
||||
}
|
||||
|
||||
return area_okay || config().create_empty_areas;
|
||||
}
|
||||
|
||||
bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) {
|
||||
set_num_members(members.size());
|
||||
osmium::builder::AreaBuilder builder{out_buffer};
|
||||
builder.initialize_from_object(relation);
|
||||
|
||||
const bool area_okay = create_rings();
|
||||
if (area_okay || config().create_empty_areas) {
|
||||
add_tags_to_area(builder, relation);
|
||||
}
|
||||
if (area_okay) {
|
||||
add_rings_to_area(builder);
|
||||
}
|
||||
|
||||
if (report_ways()) {
|
||||
for (const osmium::Way* way : members) {
|
||||
config().problem_reporter->report_way(*way);
|
||||
}
|
||||
}
|
||||
|
||||
return area_okay || config().create_empty_areas;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit AssemblerLegacy(const config_type& config) :
|
||||
detail::BasicAssemblerWithTags(config) {
|
||||
}
|
||||
|
||||
~AssemblerLegacy() noexcept = default;
|
||||
|
||||
/**
|
||||
* Assemble an area from the given way.
|
||||
* The resulting area is put into the out_buffer.
|
||||
*
|
||||
* @returns false if there was some kind of error building the
|
||||
* area, true otherwise.
|
||||
*/
|
||||
bool operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) {
|
||||
if (!config().create_way_polygons) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (way.tags().has_tag("area", "no")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (config().problem_reporter) {
|
||||
config().problem_reporter->set_object(osmium::item_type::way, way.id());
|
||||
config().problem_reporter->set_nodes(way.nodes().size());
|
||||
}
|
||||
|
||||
// Ignore (but count) ways without segments.
|
||||
if (way.nodes().size() < 2) {
|
||||
++stats().short_ways;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!way.ends_have_same_id()) {
|
||||
++stats().duplicate_nodes;
|
||||
if (config().problem_reporter) {
|
||||
config().problem_reporter->report_duplicate_node(way.nodes().front().ref(), way.nodes().back().ref(), way.nodes().front().location());
|
||||
}
|
||||
}
|
||||
|
||||
++stats().from_ways;
|
||||
stats().invalid_locations = segment_list().extract_segments_from_way(config().problem_reporter,
|
||||
stats().duplicate_nodes,
|
||||
way);
|
||||
if (!config().ignore_invalid_locations && stats().invalid_locations > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config().debug_level > 0) {
|
||||
std::cerr << "\nAssembling way " << way.id() << " containing " << segment_list().size() << " nodes\n";
|
||||
}
|
||||
|
||||
// Now create the Area object and add the attributes and tags
|
||||
// from the way.
|
||||
const bool okay = create_area(out_buffer, way);
|
||||
if (okay) {
|
||||
out_buffer.commit();
|
||||
} else {
|
||||
out_buffer.rollback();
|
||||
}
|
||||
|
||||
if (debug()) {
|
||||
std::cerr << "Done: " << stats() << "\n";
|
||||
}
|
||||
|
||||
return okay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble an area from the given relation and its members.
|
||||
* The resulting area is put into the out_buffer.
|
||||
*
|
||||
* @returns false if there was some kind of error building the
|
||||
* area(s), true otherwise.
|
||||
*/
|
||||
bool operator()(const osmium::Relation& relation, const std::vector<const osmium::Way*>& members, osmium::memory::Buffer& out_buffer) {
|
||||
assert(relation.members().size() >= members.size());
|
||||
|
||||
if (config().problem_reporter) {
|
||||
config().problem_reporter->set_object(osmium::item_type::relation, relation.id());
|
||||
}
|
||||
|
||||
if (relation.members().empty()) {
|
||||
++stats().no_way_in_mp_relation;
|
||||
return false;
|
||||
}
|
||||
|
||||
++stats().from_relations;
|
||||
stats().invalid_locations = segment_list().extract_segments_from_ways(config().problem_reporter,
|
||||
stats().duplicate_nodes,
|
||||
stats().duplicate_ways,
|
||||
relation,
|
||||
members);
|
||||
if (!config().ignore_invalid_locations && stats().invalid_locations > 0) {
|
||||
return false;
|
||||
}
|
||||
stats().member_ways = members.size();
|
||||
|
||||
if (stats().member_ways == 1) {
|
||||
++stats().single_way_in_mp_relation;
|
||||
}
|
||||
|
||||
if (config().debug_level > 0) {
|
||||
std::cerr << "\nAssembling relation " << relation.id() << " containing " << members.size() << " way members with " << segment_list().size() << " nodes\n";
|
||||
}
|
||||
|
||||
const std::size_t area_offset = out_buffer.committed();
|
||||
|
||||
// Now create the Area object and add the attributes and tags
|
||||
// from the relation.
|
||||
bool okay = create_area(out_buffer, relation, members);
|
||||
if (okay) {
|
||||
if ((config().create_new_style_polygons && stats().no_tags_on_relation == 0) ||
|
||||
(config().create_old_style_polygons && stats().no_tags_on_relation != 0)) {
|
||||
out_buffer.commit();
|
||||
} else {
|
||||
out_buffer.rollback();
|
||||
}
|
||||
} else {
|
||||
out_buffer.rollback();
|
||||
}
|
||||
|
||||
const osmium::TagList& area_tags = out_buffer.get<osmium::Area>(area_offset).tags(); // tags of the area we just built
|
||||
|
||||
// Find all closed ways that are inner rings and check their
|
||||
// tags. If they are not the same as the tags of the area we
|
||||
// just built, add them to a list and later build areas for
|
||||
// them, too.
|
||||
std::vector<const osmium::Way*> ways_that_should_be_areas;
|
||||
if (stats().wrong_role == 0) {
|
||||
detail::for_each_member(relation, members, [this, &ways_that_should_be_areas, &area_tags](const osmium::RelationMember& member, const osmium::Way& way) {
|
||||
if (!std::strcmp(member.role(), "inner")) {
|
||||
if (!way.nodes().empty() && way.is_closed() && way.tags().size() > 0) {
|
||||
const auto d = std::count_if(way.tags().cbegin(), way.tags().cend(), std::cref(filter()));
|
||||
if (d > 0) {
|
||||
osmium::tags::KeyFilter::iterator way_fi_begin(std::cref(filter()), way.tags().cbegin(), way.tags().cend());
|
||||
osmium::tags::KeyFilter::iterator way_fi_end(std::cref(filter()), way.tags().cend(), way.tags().cend());
|
||||
osmium::tags::KeyFilter::iterator area_fi_begin(std::cref(filter()), area_tags.cbegin(), area_tags.cend());
|
||||
osmium::tags::KeyFilter::iterator area_fi_end(std::cref(filter()), area_tags.cend(), area_tags.cend());
|
||||
|
||||
if (!std::equal(way_fi_begin, way_fi_end, area_fi_begin) || d != std::distance(area_fi_begin, area_fi_end)) {
|
||||
ways_that_should_be_areas.push_back(&way);
|
||||
} else {
|
||||
++stats().inner_with_same_tags;
|
||||
if (config().problem_reporter) {
|
||||
config().problem_reporter->report_inner_with_same_tags(way);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (debug()) {
|
||||
std::cerr << "Done: " << stats() << "\n";
|
||||
}
|
||||
|
||||
// Now build areas for all ways found in the last step.
|
||||
for (const osmium::Way* way : ways_that_should_be_areas) {
|
||||
AssemblerLegacy assembler{config()};
|
||||
if (!assembler(*way, out_buffer)) {
|
||||
okay = false;
|
||||
}
|
||||
stats() += assembler.stats();
|
||||
}
|
||||
|
||||
return okay;
|
||||
}
|
||||
|
||||
}; // class AssemblerLegacy
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_ASSEMBLER_LEGACY_HPP
|
||||
File diff suppressed because it is too large
Load Diff
+93
@@ -0,0 +1,93 @@
|
||||
#ifndef OSMIUM_AREA_DETAIL_BASIC_ASSEMBLER_WITH_TAGS_HPP
|
||||
#define OSMIUM_AREA_DETAIL_BASIC_ASSEMBLER_WITH_TAGS_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <osmium/area/assembler_config.hpp>
|
||||
#include <osmium/area/detail/basic_assembler.hpp>
|
||||
#include <osmium/area/stats.hpp>
|
||||
#include <osmium/builder/osm_object_builder.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace area {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class BasicAssemblerWithTags : public detail::BasicAssembler {
|
||||
|
||||
protected:
|
||||
|
||||
bool report_ways() const noexcept {
|
||||
if (!config().problem_reporter) {
|
||||
return false;
|
||||
}
|
||||
return stats().duplicate_nodes ||
|
||||
stats().duplicate_segments ||
|
||||
stats().intersections ||
|
||||
stats().open_rings ||
|
||||
stats().short_ways ||
|
||||
stats().touching_rings ||
|
||||
stats().ways_in_multiple_rings ||
|
||||
stats().wrong_role;
|
||||
}
|
||||
|
||||
static void copy_tags_without_type(osmium::builder::AreaBuilder& builder, const osmium::TagList& tags) {
|
||||
osmium::builder::TagListBuilder tl_builder{builder};
|
||||
for (const osmium::Tag& tag : tags) {
|
||||
if (std::strcmp(tag.key(), "type")) {
|
||||
tl_builder.add_tag(tag.key(), tag.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
using config_type = osmium::area::AssemblerConfig;
|
||||
|
||||
explicit BasicAssemblerWithTags(const config_type& config) :
|
||||
BasicAssembler(config) {
|
||||
}
|
||||
|
||||
}; // class BasicAssemblerWithTags
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_DETAIL_BASIC_ASSEMBLER_WITH_TAGS_HPP
|
||||
@@ -0,0 +1,389 @@
|
||||
#ifndef OSMIUM_AREA_DETAIL_NODE_REF_SEGMENT_HPP
|
||||
#define OSMIUM_AREA_DETAIL_NODE_REF_SEGMENT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/area/detail/vector.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
class Way;
|
||||
|
||||
namespace area {
|
||||
|
||||
/**
|
||||
* @brief Namespace for Osmium internal use
|
||||
*/
|
||||
namespace detail {
|
||||
|
||||
class ProtoRing;
|
||||
|
||||
enum class role_type : uint8_t {
|
||||
unknown = 0,
|
||||
outer = 1,
|
||||
inner = 2,
|
||||
empty = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* This helper class for the Assembler class models a segment,
|
||||
* the connection between two nodes.
|
||||
*
|
||||
* Internally segments have their smaller coordinate at the
|
||||
* beginning of the segment. Smaller, in this case, means smaller
|
||||
* x coordinate, and, if they are the same, smaller y coordinate.
|
||||
*/
|
||||
class NodeRefSegment {
|
||||
|
||||
// First node in order described above.
|
||||
osmium::NodeRef m_first;
|
||||
|
||||
// Second node in order described above.
|
||||
osmium::NodeRef m_second;
|
||||
|
||||
// Way this segment was from.
|
||||
const osmium::Way* m_way;
|
||||
|
||||
// The ring this segment is part of. Initially nullptr, this
|
||||
// will be filled in once we know which ring the segment is in.
|
||||
ProtoRing* m_ring;
|
||||
|
||||
// The role of this segment from the member role.
|
||||
role_type m_role;
|
||||
|
||||
// Nodes have to be reversed to get the intended order.
|
||||
bool m_reverse = false;
|
||||
|
||||
// We found the right direction for this segment in the ring.
|
||||
// (This depends on whether it is an inner or outer ring.)
|
||||
bool m_direction_done = false;
|
||||
|
||||
public:
|
||||
|
||||
NodeRefSegment() noexcept :
|
||||
m_first(),
|
||||
m_second(),
|
||||
m_way(nullptr),
|
||||
m_ring(nullptr),
|
||||
m_role(role_type::unknown) {
|
||||
}
|
||||
|
||||
NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, role_type role, const osmium::Way* way) noexcept :
|
||||
m_first(nr1),
|
||||
m_second(nr2),
|
||||
m_way(way),
|
||||
m_ring(nullptr),
|
||||
m_role(role) {
|
||||
if (nr2.location() < nr1.location()) {
|
||||
using std::swap;
|
||||
swap(m_first, m_second);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The ring this segment is a part of. nullptr if we don't
|
||||
* have the ring yet.
|
||||
*/
|
||||
ProtoRing* ring() const noexcept {
|
||||
return m_ring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the segment has already been placed in a
|
||||
* ring.
|
||||
*/
|
||||
bool is_done() const noexcept {
|
||||
return m_ring != nullptr;
|
||||
}
|
||||
|
||||
void set_ring(ProtoRing* ring) noexcept {
|
||||
assert(ring);
|
||||
m_ring = ring;
|
||||
}
|
||||
|
||||
bool is_reverse() const noexcept {
|
||||
return m_reverse;
|
||||
}
|
||||
|
||||
void reverse() noexcept {
|
||||
m_reverse = !m_reverse;
|
||||
}
|
||||
|
||||
bool is_direction_done() const noexcept {
|
||||
return m_direction_done;
|
||||
}
|
||||
|
||||
void mark_direction_done() noexcept {
|
||||
m_direction_done = true;
|
||||
}
|
||||
|
||||
void mark_direction_not_done() noexcept {
|
||||
m_direction_done = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first NodeRef of Segment according to sorting
|
||||
* order (bottom left to top right).
|
||||
*/
|
||||
const osmium::NodeRef& first() const noexcept {
|
||||
return m_first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return second NodeRef of Segment according to sorting
|
||||
* order (bottom left to top right).
|
||||
*/
|
||||
const osmium::NodeRef& second() const noexcept {
|
||||
return m_second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return real first NodeRef of Segment.
|
||||
*/
|
||||
const osmium::NodeRef& start() const noexcept {
|
||||
return m_reverse ? m_second : m_first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return real second NodeRef of Segment.
|
||||
*/
|
||||
const osmium::NodeRef& stop() const noexcept {
|
||||
return m_reverse ? m_first : m_second;
|
||||
}
|
||||
|
||||
bool role_outer() const noexcept {
|
||||
return m_role == role_type::outer;
|
||||
}
|
||||
|
||||
bool role_inner() const noexcept {
|
||||
return m_role == role_type::inner;
|
||||
}
|
||||
|
||||
bool role_empty() const noexcept {
|
||||
return m_role == role_type::empty;
|
||||
}
|
||||
|
||||
const char* role_name() const noexcept {
|
||||
static const char* names[] = { "unknown", "outer", "inner", "empty" };
|
||||
return names[int(m_role)];
|
||||
}
|
||||
|
||||
const osmium::Way* way() const noexcept {
|
||||
return m_way;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "determinant" of this segment. Used for calculating
|
||||
* the winding order or a ring.
|
||||
*/
|
||||
int64_t det() const noexcept {
|
||||
const vec a{start()};
|
||||
const vec b{stop()};
|
||||
return a * b;
|
||||
}
|
||||
|
||||
}; // class NodeRefSegment
|
||||
|
||||
/// NodeRefSegments are equal if both their locations are equal
|
||||
inline bool operator==(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
|
||||
return lhs.first().location() == rhs.first().location() &&
|
||||
lhs.second().location() == rhs.second().location();
|
||||
}
|
||||
|
||||
inline bool operator!=(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
|
||||
return ! (lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* A NodeRefSegment is "smaller" if the first point is to the
|
||||
* left and down of the first point of the second segment.
|
||||
* If both first points are the same, the segment with the higher
|
||||
* slope comes first. If the slope is the same, the shorter
|
||||
* segment comes first.
|
||||
*/
|
||||
inline bool operator<(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
|
||||
if (lhs.first().location() == rhs.first().location()) {
|
||||
const vec p0{lhs.first().location()};
|
||||
const vec p1{lhs.second().location()};
|
||||
const vec q0{rhs.first().location()};
|
||||
const vec q1{rhs.second().location()};
|
||||
const vec p = p1 - p0;
|
||||
const vec q = q1 - q0;
|
||||
|
||||
if (p.x == 0 && q.x == 0) {
|
||||
return p.y < q.y;
|
||||
}
|
||||
|
||||
const auto a = p.y * q.x;
|
||||
const auto b = q.y * p.x;
|
||||
if (a == b) {
|
||||
return p.x < q.x;
|
||||
}
|
||||
return a > b;
|
||||
}
|
||||
return lhs.first().location() < rhs.first().location();
|
||||
}
|
||||
|
||||
template <typename TChar, typename TTraits>
|
||||
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const NodeRefSegment& segment) {
|
||||
return out << segment.start() << "--" << segment.stop()
|
||||
<< "[" << (segment.is_reverse() ? 'R' : '_')
|
||||
<< (segment.is_done() ? 'd' : '_')
|
||||
<< (segment.is_direction_done() ? 'D' : '_') << "]";
|
||||
}
|
||||
|
||||
inline bool outside_x_range(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
|
||||
return s1.first().location().x() > s2.second().location().x();
|
||||
}
|
||||
|
||||
inline bool y_range_overlap(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
|
||||
const std::pair<int32_t, int32_t> m1 = std::minmax(s1.first().location().y(), s1.second().location().y());
|
||||
const std::pair<int32_t, int32_t> m2 = std::minmax(s2.first().location().y(), s2.second().location().y());
|
||||
return !(m1.first > m2.second || m2.first > m1.second);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the intersection between two NodeRefSegments. The
|
||||
* result is returned as a Location. Note that because the Location
|
||||
* uses integers with limited precision internally, the result
|
||||
* might be slightly different than the numerically correct
|
||||
* location.
|
||||
*
|
||||
* This function uses integer arithmetic as much as possible and
|
||||
* will not work if the segments are longer than about half the
|
||||
* planet. This shouldn't happen with real data, so it isn't a big
|
||||
* problem.
|
||||
*
|
||||
* If the segments touch in one or both of their endpoints, it
|
||||
* doesn't count as an intersection.
|
||||
*
|
||||
* If the segments intersect not in a single point but in multiple
|
||||
* points, ie if they are collinear and overlap, the smallest
|
||||
* of the endpoints that is in the overlapping section is returned.
|
||||
*
|
||||
* @returns Undefined osmium::Location if there is no intersection
|
||||
* or a defined Location if the segments intersect.
|
||||
*/
|
||||
inline osmium::Location calculate_intersection(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
|
||||
// See http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
|
||||
// for some hints about how the algorithm works.
|
||||
const vec p0{s1.first()};
|
||||
const vec p1{s1.second()};
|
||||
const vec q0{s2.first()};
|
||||
const vec q1{s2.second()};
|
||||
|
||||
if ((p0 == q0 && p1 == q1) ||
|
||||
(p0 == q1 && p1 == q0)) {
|
||||
// segments are the same
|
||||
return osmium::Location{};
|
||||
}
|
||||
|
||||
const vec pd = p1 - p0;
|
||||
const int64_t d = pd * (q1 - q0);
|
||||
|
||||
if (d != 0) {
|
||||
// segments are not collinear
|
||||
|
||||
if (p0 == q0 || p0 == q1 || p1 == q0 || p1 == q1) {
|
||||
// touching at an end point
|
||||
return osmium::Location{};
|
||||
}
|
||||
|
||||
// intersection in a point
|
||||
|
||||
const int64_t na = (q1.x - q0.x) * (p0.y - q0.y) -
|
||||
(q1.y - q0.y) * (p0.x - q0.x);
|
||||
|
||||
const int64_t nb = (p1.x - p0.x) * (p0.y - q0.y) -
|
||||
(p1.y - p0.y) * (p0.x - q0.x);
|
||||
|
||||
if ((d > 0 && na >= 0 && na <= d && nb >= 0 && nb <= d) ||
|
||||
(d < 0 && na <= 0 && na >= d && nb <= 0 && nb >= d)) {
|
||||
const double ua = double(na) / d;
|
||||
const vec i = p0 + ua * (p1 - p0);
|
||||
return osmium::Location{int32_t(i.x), int32_t(i.y)};
|
||||
}
|
||||
|
||||
return osmium::Location{};
|
||||
}
|
||||
|
||||
// segments are collinear
|
||||
|
||||
if (pd * (q0 - p0) == 0) {
|
||||
// segments are on the same line
|
||||
|
||||
struct seg_loc {
|
||||
int segment;
|
||||
osmium::Location location;
|
||||
};
|
||||
|
||||
seg_loc sl[4];
|
||||
sl[0] = {0, s1.first().location() };
|
||||
sl[1] = {0, s1.second().location()};
|
||||
sl[2] = {1, s2.first().location() };
|
||||
sl[3] = {1, s2.second().location()};
|
||||
|
||||
std::sort(sl, sl+4, [](const seg_loc& lhs, const seg_loc& rhs) {
|
||||
return lhs.location < rhs.location;
|
||||
});
|
||||
|
||||
if (sl[1].location == sl[2].location) {
|
||||
return osmium::Location();
|
||||
}
|
||||
|
||||
if (sl[0].segment != sl[1].segment) {
|
||||
if (sl[0].location == sl[1].location) {
|
||||
return sl[2].location;
|
||||
}
|
||||
return sl[1].location;
|
||||
}
|
||||
}
|
||||
|
||||
return osmium::Location{};
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_DETAIL_NODE_REF_SEGMENT_HPP
|
||||
@@ -0,0 +1,243 @@
|
||||
#ifndef OSMIUM_AREA_DETAIL_PROTO_RING_HPP
|
||||
#define OSMIUM_AREA_DETAIL_PROTO_RING_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/area/detail/node_ref_segment.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
class Way;
|
||||
|
||||
namespace area {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* A ring in the process of being built by the Assembler object.
|
||||
*/
|
||||
class ProtoRing {
|
||||
|
||||
public:
|
||||
|
||||
using segments_type = std::vector<NodeRefSegment*>;
|
||||
|
||||
private:
|
||||
|
||||
// Segments in this ring.
|
||||
segments_type m_segments;
|
||||
|
||||
// If this is an outer ring, these point to it's inner rings
|
||||
// (if any).
|
||||
std::vector<ProtoRing*> m_inner;
|
||||
|
||||
// The smallest segment. Will be kept current whenever a new
|
||||
// segment is added to the ring.
|
||||
NodeRefSegment* m_min_segment;
|
||||
|
||||
// If this is an inner ring, points to the outer ring.
|
||||
ProtoRing* m_outer_ring;
|
||||
|
||||
#ifdef OSMIUM_DEBUG_RING_NO
|
||||
static int64_t next_num() noexcept {
|
||||
static int64_t counter = 0;
|
||||
return ++counter;
|
||||
}
|
||||
|
||||
int64_t m_num;
|
||||
#endif
|
||||
|
||||
int64_t m_sum;
|
||||
|
||||
public:
|
||||
|
||||
explicit ProtoRing(NodeRefSegment* segment) noexcept :
|
||||
m_segments(),
|
||||
m_inner(),
|
||||
m_min_segment(segment),
|
||||
m_outer_ring(nullptr),
|
||||
#ifdef OSMIUM_DEBUG_RING_NO
|
||||
m_num(next_num()),
|
||||
#endif
|
||||
m_sum(0) {
|
||||
add_segment_back(segment);
|
||||
}
|
||||
|
||||
void add_segment_back(NodeRefSegment* segment) {
|
||||
assert(segment);
|
||||
if (*segment < *m_min_segment) {
|
||||
m_min_segment = segment;
|
||||
}
|
||||
m_segments.push_back(segment);
|
||||
segment->set_ring(this);
|
||||
m_sum += segment->det();
|
||||
}
|
||||
|
||||
NodeRefSegment* min_segment() const noexcept {
|
||||
return m_min_segment;
|
||||
}
|
||||
|
||||
ProtoRing* outer_ring() const noexcept {
|
||||
return m_outer_ring;
|
||||
}
|
||||
|
||||
void set_outer_ring(ProtoRing* outer_ring) noexcept {
|
||||
assert(outer_ring);
|
||||
assert(m_inner.empty());
|
||||
m_outer_ring = outer_ring;
|
||||
}
|
||||
|
||||
const std::vector<ProtoRing*>& inner_rings() const noexcept {
|
||||
return m_inner;
|
||||
}
|
||||
|
||||
void add_inner_ring(ProtoRing* ring) {
|
||||
assert(ring);
|
||||
assert(!m_outer_ring);
|
||||
m_inner.push_back(ring);
|
||||
}
|
||||
|
||||
bool is_outer() const noexcept {
|
||||
return !m_outer_ring;
|
||||
}
|
||||
|
||||
const segments_type& segments() const noexcept {
|
||||
return m_segments;
|
||||
}
|
||||
|
||||
const NodeRef& get_node_ref_start() const noexcept {
|
||||
return m_segments.front()->start();
|
||||
}
|
||||
|
||||
const NodeRef& get_node_ref_stop() const noexcept {
|
||||
return m_segments.back()->stop();
|
||||
}
|
||||
|
||||
bool closed() const noexcept {
|
||||
return get_node_ref_start().location() == get_node_ref_stop().location();
|
||||
}
|
||||
|
||||
void reverse() {
|
||||
std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) {
|
||||
segment->reverse();
|
||||
});
|
||||
std::reverse(m_segments.begin(), m_segments.end());
|
||||
m_sum = -m_sum;
|
||||
}
|
||||
|
||||
void mark_direction_done() {
|
||||
std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) {
|
||||
segment->mark_direction_done();
|
||||
});
|
||||
}
|
||||
|
||||
bool is_cw() const noexcept {
|
||||
return m_sum <= 0;
|
||||
}
|
||||
|
||||
int64_t sum() const noexcept {
|
||||
return m_sum;
|
||||
}
|
||||
|
||||
void fix_direction() noexcept {
|
||||
if (is_cw() == is_outer()) {
|
||||
reverse();
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_inner.clear();
|
||||
m_outer_ring = nullptr;
|
||||
std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) {
|
||||
segment->mark_direction_not_done();
|
||||
});
|
||||
}
|
||||
|
||||
void get_ways(std::set<const osmium::Way*>& ways) const {
|
||||
for (const auto& segment : m_segments) {
|
||||
ways.insert(segment->way());
|
||||
}
|
||||
}
|
||||
|
||||
void join_forward(ProtoRing& other) {
|
||||
for (NodeRefSegment* segment : other.m_segments) {
|
||||
add_segment_back(segment);
|
||||
}
|
||||
}
|
||||
|
||||
void join_backward(ProtoRing& other) {
|
||||
for (auto it = other.m_segments.rbegin(); it != other.m_segments.rend(); ++it) {
|
||||
(*it)->reverse();
|
||||
add_segment_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void print(std::ostream& out) const {
|
||||
#ifdef OSMIUM_DEBUG_RING_NO
|
||||
out << "Ring #" << m_num << " [";
|
||||
#else
|
||||
out << "Ring [";
|
||||
#endif
|
||||
if (!m_segments.empty()) {
|
||||
out << m_segments.front()->start().ref();
|
||||
}
|
||||
for (const auto& segment : m_segments) {
|
||||
out << ',' << segment->stop().ref();
|
||||
}
|
||||
out << "]-" << (is_outer() ? "OUTER" : "INNER");
|
||||
}
|
||||
|
||||
}; // class ProtoRing
|
||||
|
||||
template <typename TChar, typename TTraits>
|
||||
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const ProtoRing& ring) {
|
||||
ring.print(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_DETAIL_PROTO_RING_HPP
|
||||
@@ -0,0 +1,363 @@
|
||||
#ifndef OSMIUM_AREA_DETAIL_SEGMENT_LIST_HPP
|
||||
#define OSMIUM_AREA_DETAIL_SEGMENT_LIST_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/area/detail/node_ref_segment.hpp>
|
||||
#include <osmium/area/problem_reporter.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace area {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Iterate over all relation members and the vector of ways at the
|
||||
* same time and call given function with the relation member and
|
||||
* way as parameter. This takes into account that there might be
|
||||
* non-way members in the relation.
|
||||
*/
|
||||
template <typename F>
|
||||
inline void for_each_member(const osmium::Relation& relation, const std::vector<const osmium::Way*>& ways, F&& func) {
|
||||
auto way_it = ways.cbegin();
|
||||
for (const osmium::RelationMember& member : relation.members()) {
|
||||
if (member.type() == osmium::item_type::way) {
|
||||
assert(way_it != ways.cend());
|
||||
func(member, **way_it);
|
||||
++way_it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper class for the area assembler. It models
|
||||
* a list of segments.
|
||||
*/
|
||||
class SegmentList {
|
||||
|
||||
using slist_type = std::vector<NodeRefSegment>;
|
||||
|
||||
slist_type m_segments;
|
||||
|
||||
bool m_debug;
|
||||
|
||||
static role_type parse_role(const char* role) noexcept {
|
||||
if (role[0] == '\0') {
|
||||
return role_type::empty;
|
||||
}
|
||||
if (!std::strcmp(role, "outer")) {
|
||||
return role_type::outer;
|
||||
}
|
||||
if (!std::strcmp(role, "inner")) {
|
||||
return role_type::inner;
|
||||
}
|
||||
return role_type::unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the number of segments in all the ways together.
|
||||
*/
|
||||
static std::size_t get_num_segments(const std::vector<const osmium::Way*>& members) noexcept {
|
||||
return std::accumulate(members.cbegin(), members.cend(), static_cast<std::size_t>(0), [](std::size_t sum, const osmium::Way* way) {
|
||||
if (way->nodes().empty()) {
|
||||
return sum;
|
||||
}
|
||||
return sum + way->nodes().size() - 1;
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t extract_segments_from_way_impl(ProblemReporter* problem_reporter, uint64_t& duplicate_nodes, const osmium::Way& way, role_type role) {
|
||||
uint32_t invalid_locations = 0;
|
||||
|
||||
osmium::NodeRef previous_nr;
|
||||
for (const osmium::NodeRef& nr : way.nodes()) {
|
||||
if (!nr.location().valid()) {
|
||||
++invalid_locations;
|
||||
if (problem_reporter) {
|
||||
problem_reporter->report_invalid_location(way.id(), nr.ref());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (previous_nr.location()) {
|
||||
if (previous_nr.location() != nr.location()) {
|
||||
m_segments.emplace_back(previous_nr, nr, role, &way);
|
||||
} else {
|
||||
++duplicate_nodes;
|
||||
if (problem_reporter) {
|
||||
problem_reporter->report_duplicate_node(previous_nr.ref(), nr.ref(), nr.location());
|
||||
}
|
||||
}
|
||||
}
|
||||
previous_nr = nr;
|
||||
}
|
||||
|
||||
return invalid_locations;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit SegmentList(bool debug) noexcept :
|
||||
m_segments(),
|
||||
m_debug(debug) {
|
||||
}
|
||||
|
||||
~SegmentList() noexcept = default;
|
||||
|
||||
SegmentList(const SegmentList&) = delete;
|
||||
SegmentList(SegmentList&&) = delete;
|
||||
|
||||
SegmentList& operator=(const SegmentList&) = delete;
|
||||
SegmentList& operator=(SegmentList&&) = delete;
|
||||
|
||||
/// The number of segments in the list.
|
||||
std::size_t size() const noexcept {
|
||||
return m_segments.size();
|
||||
}
|
||||
|
||||
/// Is the segment list empty?
|
||||
bool empty() const noexcept {
|
||||
return m_segments.empty();
|
||||
}
|
||||
|
||||
using const_iterator = slist_type::const_iterator;
|
||||
using iterator = slist_type::iterator;
|
||||
|
||||
NodeRefSegment& front() {
|
||||
return m_segments.front();
|
||||
}
|
||||
|
||||
NodeRefSegment& back() {
|
||||
return m_segments.back();
|
||||
}
|
||||
|
||||
const NodeRefSegment& operator[](std::size_t n) const noexcept {
|
||||
assert(n < m_segments.size());
|
||||
return m_segments[n];
|
||||
}
|
||||
|
||||
NodeRefSegment& operator[](std::size_t n) noexcept {
|
||||
assert(n < m_segments.size());
|
||||
return m_segments[n];
|
||||
}
|
||||
|
||||
iterator begin() noexcept {
|
||||
return m_segments.begin();
|
||||
}
|
||||
|
||||
iterator end() noexcept {
|
||||
return m_segments.end();
|
||||
}
|
||||
|
||||
const_iterator begin() const noexcept {
|
||||
return m_segments.begin();
|
||||
}
|
||||
|
||||
const_iterator end() const noexcept {
|
||||
return m_segments.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable debug output to stderr. This is used
|
||||
* for debugging libosmium itself.
|
||||
*/
|
||||
void enable_debug_output(bool debug = true) noexcept {
|
||||
m_debug = debug;
|
||||
}
|
||||
|
||||
/// Sort the list of segments.
|
||||
void sort() {
|
||||
std::sort(m_segments.begin(), m_segments.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract segments from given way and add them to the list.
|
||||
*
|
||||
* Segments connecting two nodes with the same location (ie
|
||||
* same node or different nodes with same location) are
|
||||
* removed after reporting the duplicate node.
|
||||
*/
|
||||
uint32_t extract_segments_from_way(ProblemReporter* problem_reporter, uint64_t& duplicate_nodes, const osmium::Way& way) {
|
||||
if (way.nodes().empty()) {
|
||||
return 0;
|
||||
}
|
||||
m_segments.reserve(way.nodes().size() - 1);
|
||||
return extract_segments_from_way_impl(problem_reporter, duplicate_nodes, way, role_type::outer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all segments from all ways that make up this
|
||||
* multipolygon relation and add them to the list.
|
||||
*/
|
||||
uint32_t extract_segments_from_ways(ProblemReporter* problem_reporter,
|
||||
uint64_t& duplicate_nodes,
|
||||
uint64_t& duplicate_ways,
|
||||
const osmium::Relation& relation,
|
||||
const std::vector<const osmium::Way*>& members) {
|
||||
assert(relation.cmembers().size() >= members.size());
|
||||
|
||||
const std::size_t num_segments = get_num_segments(members);
|
||||
if (problem_reporter) {
|
||||
problem_reporter->set_nodes(num_segments);
|
||||
}
|
||||
m_segments.reserve(num_segments);
|
||||
|
||||
std::unordered_set<osmium::object_id_type> ids;
|
||||
ids.reserve(members.size());
|
||||
uint32_t invalid_locations = 0;
|
||||
for_each_member(relation, members, [&](const osmium::RelationMember& member, const osmium::Way& way) {
|
||||
if (ids.count(way.id()) == 0) {
|
||||
ids.insert(way.id());
|
||||
const auto role = parse_role(member.role());
|
||||
invalid_locations += extract_segments_from_way_impl(problem_reporter, duplicate_nodes, way, role);
|
||||
} else {
|
||||
++duplicate_ways;
|
||||
if (problem_reporter) {
|
||||
problem_reporter->report_duplicate_way(way);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return invalid_locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find duplicate segments (ie same start and end point) in the
|
||||
* list and remove them. This will always remove pairs of the
|
||||
* same segment. So if there are three, for instance, two will
|
||||
* be removed and one will be left.
|
||||
*/
|
||||
void erase_duplicate_segments(ProblemReporter* problem_reporter, uint64_t& duplicate_segments, uint64_t& overlapping_segments) {
|
||||
while (true) {
|
||||
auto it = std::adjacent_find(m_segments.begin(), m_segments.end());
|
||||
if (it == m_segments.end()) {
|
||||
break;
|
||||
}
|
||||
if (m_debug) {
|
||||
std::cerr << " erase duplicate segment: " << *it << "\n";
|
||||
}
|
||||
|
||||
// Only count and report duplicate segments if they
|
||||
// belong to the same way or if they don't both have
|
||||
// the role "inner". Those cases are definitely wrong.
|
||||
// If the duplicate segments belong to different
|
||||
// "inner" ways, they could be touching inner rings
|
||||
// which are perfectly okay. Note that for this check
|
||||
// the role has to be correct in the member data.
|
||||
if (it->way() == std::next(it)->way() || !it->role_inner() || !std::next(it)->role_inner()) {
|
||||
++duplicate_segments;
|
||||
if (problem_reporter) {
|
||||
problem_reporter->report_duplicate_segment(it->first(), it->second());
|
||||
}
|
||||
}
|
||||
|
||||
if (it+2 != m_segments.end() && *it == *(it+2)) {
|
||||
++overlapping_segments;
|
||||
if (problem_reporter) {
|
||||
problem_reporter->report_overlapping_segment(it->first(), it->second());
|
||||
}
|
||||
}
|
||||
|
||||
m_segments.erase(it, it+2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find intersection between segments.
|
||||
*
|
||||
* @param problem_reporter Any intersections found are
|
||||
* reported to this object.
|
||||
* @returns true if there are intersections.
|
||||
*/
|
||||
uint32_t find_intersections(ProblemReporter* problem_reporter) const {
|
||||
if (m_segments.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t found_intersections = 0;
|
||||
|
||||
for (auto it1 = m_segments.cbegin(); it1 != m_segments.cend() - 1; ++it1) {
|
||||
const NodeRefSegment& s1 = *it1;
|
||||
for (auto it2 = it1+1; it2 != m_segments.end(); ++it2) {
|
||||
const NodeRefSegment& s2 = *it2;
|
||||
|
||||
assert(s1 != s2); // erase_duplicate_segments() should have made sure of that
|
||||
|
||||
if (outside_x_range(s2, s1)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (y_range_overlap(s1, s2)) {
|
||||
osmium::Location intersection{calculate_intersection(s1, s2)};
|
||||
if (intersection) {
|
||||
++found_intersections;
|
||||
if (m_debug) {
|
||||
std::cerr << " segments " << s1 << " and " << s2 << " intersecting at " << intersection << "\n";
|
||||
}
|
||||
if (problem_reporter) {
|
||||
problem_reporter->report_intersection(s1.way()->id(), s1.first().location(), s1.second().location(),
|
||||
s2.way()->id(), s2.first().location(), s2.second().location(), intersection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found_intersections;
|
||||
}
|
||||
|
||||
}; // class SegmentList
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_DETAIL_SEGMENT_LIST_HPP
|
||||
@@ -0,0 +1,121 @@
|
||||
#ifndef OSMIUM_AREA_DETAIL_VECTOR_HPP
|
||||
#define OSMIUM_AREA_DETAIL_VECTOR_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace area {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* This helper class models a 2D vector in the mathematical sense.
|
||||
* It uses 64 bit integers internally which has enough precision
|
||||
* for most operations with inputs based on 32 bit locations.
|
||||
*/
|
||||
struct vec {
|
||||
|
||||
int64_t x;
|
||||
int64_t y;
|
||||
|
||||
constexpr vec(int64_t a, int64_t b) noexcept :
|
||||
x(a),
|
||||
y(b) {
|
||||
}
|
||||
|
||||
constexpr explicit vec(const osmium::Location& l) noexcept :
|
||||
x(l.x()),
|
||||
y(l.y()) {
|
||||
}
|
||||
|
||||
constexpr explicit vec(const osmium::NodeRef& nr) noexcept :
|
||||
x(nr.x()),
|
||||
y(nr.y()) {
|
||||
}
|
||||
|
||||
}; // struct vec
|
||||
|
||||
// addition
|
||||
constexpr inline vec operator+(const vec& lhs, const vec& rhs) noexcept {
|
||||
return vec{lhs.x + rhs.x, lhs.y + rhs.y};
|
||||
}
|
||||
|
||||
// subtraction
|
||||
constexpr inline vec operator-(const vec& lhs, const vec& rhs) noexcept {
|
||||
return vec{lhs.x - rhs.x, lhs.y - rhs.y};
|
||||
}
|
||||
|
||||
// cross product
|
||||
constexpr inline int64_t operator*(const vec& lhs, const vec& rhs) noexcept {
|
||||
return lhs.x * rhs.y - lhs.y * rhs.x;
|
||||
}
|
||||
|
||||
// scale vector
|
||||
constexpr inline vec operator*(double s, const vec& v) noexcept {
|
||||
return vec{int64_t(s * v.x), int64_t(s * v.y)};
|
||||
}
|
||||
|
||||
// scale vector
|
||||
constexpr inline vec operator*(const vec& v, double s) noexcept {
|
||||
return vec{int64_t(s * v.x), int64_t(s * v.y)};
|
||||
}
|
||||
|
||||
// equality
|
||||
constexpr inline bool operator==(const vec& lhs, const vec& rhs) noexcept {
|
||||
return lhs.x == rhs.x && lhs.y == rhs.y;
|
||||
}
|
||||
|
||||
// inequality
|
||||
constexpr inline bool operator!=(const vec& lhs, const vec& rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template <typename TChar, typename TTraits>
|
||||
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const vec& v) {
|
||||
return out << '(' << v.x << ',' << v.y << ')';
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_DETAIL_VECTOR_HPP
|
||||
@@ -0,0 +1,132 @@
|
||||
#ifndef OSMIUM_AREA_GEOM_ASSEMBLER_HPP
|
||||
#define OSMIUM_AREA_GEOM_ASSEMBLER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/area/assembler_config.hpp>
|
||||
#include <osmium/area/detail/basic_assembler.hpp>
|
||||
#include <osmium/area/detail/segment_list.hpp>
|
||||
#include <osmium/area/stats.hpp>
|
||||
#include <osmium/builder/osm_object_builder.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace area {
|
||||
|
||||
/**
|
||||
* Assembles area objects from closed ways or multipolygon relations
|
||||
* and their members. Unlike the Assembler, this one doesn't take
|
||||
* tags into account at all. And it doesn't interpret all the config
|
||||
* settings and doesn't do all the checks and error reporting the
|
||||
* Assembler does.
|
||||
*
|
||||
* This class was developed specifically for the need of osm2pgsql.
|
||||
* Unless you know what you are doing, use the Assembler class instead
|
||||
* of this class. Contact the Libosmium developers if you want to use
|
||||
* this class.
|
||||
*/
|
||||
class GeomAssembler : public detail::BasicAssembler {
|
||||
|
||||
public:
|
||||
|
||||
using config_type = osmium::area::AssemblerConfig;
|
||||
|
||||
explicit GeomAssembler(const config_type& config) :
|
||||
detail::BasicAssembler(config) {
|
||||
}
|
||||
|
||||
~GeomAssembler() noexcept = default;
|
||||
|
||||
/**
|
||||
* Assemble an area from the given way.
|
||||
*
|
||||
* The resulting area is put into the out_buffer.
|
||||
*
|
||||
* @returns false if there was some kind of error building the
|
||||
* area, true otherwise.
|
||||
*/
|
||||
bool operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) {
|
||||
segment_list().extract_segments_from_way(config().problem_reporter, stats().duplicate_nodes, way);
|
||||
|
||||
if (!create_rings()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
osmium::builder::AreaBuilder builder{out_buffer};
|
||||
builder.initialize_from_object(way);
|
||||
add_rings_to_area(builder);
|
||||
}
|
||||
out_buffer.commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble an area from the given relation and its member ways
|
||||
* which are in the ways_buffer.
|
||||
*
|
||||
* The resulting area is put into the out_buffer.
|
||||
*
|
||||
* @returns false if there was some kind of error building the
|
||||
* area, true otherwise.
|
||||
*/
|
||||
bool operator()(const osmium::Relation& relation, const osmium::memory::Buffer& ways_buffer, osmium::memory::Buffer& out_buffer) {
|
||||
for (const auto& way : ways_buffer.select<osmium::Way>()) {
|
||||
segment_list().extract_segments_from_way(config().problem_reporter, stats().duplicate_nodes, way);
|
||||
}
|
||||
|
||||
if (!create_rings()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
osmium::builder::AreaBuilder builder{out_buffer};
|
||||
builder.initialize_from_object(relation);
|
||||
add_rings_to_area(builder);
|
||||
}
|
||||
out_buffer.commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // class GeomAssembler
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_GEOM_ASSEMBLER_HPP
|
||||
@@ -0,0 +1,210 @@
|
||||
#ifndef OSMIUM_AREA_MULTIPOLYGON_COLLECTOR_HPP
|
||||
#define OSMIUM_AREA_MULTIPOLYGON_COLLECTOR_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/area/stats.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/relations/collector.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace relations {
|
||||
class RelationMeta;
|
||||
} // namespace relations
|
||||
|
||||
/**
|
||||
* @brief Code related to the building of areas (multipolygons) from relations.
|
||||
*/
|
||||
namespace area {
|
||||
|
||||
/**
|
||||
* This class collects all data needed for creating areas from
|
||||
* relations tagged with type=multipolygon or type=boundary.
|
||||
* Most of its functionality is derived from the parent class
|
||||
* osmium::relations::Collector.
|
||||
*
|
||||
* The actual assembling of the areas is done by the assembler
|
||||
* class given as template argument.
|
||||
*
|
||||
* @tparam TAssembler Multipolygon Assembler class.
|
||||
* @pre The Ids of all objects must be unique in the input data.
|
||||
*/
|
||||
template <typename TAssembler>
|
||||
class MultipolygonCollector : public osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false> {
|
||||
|
||||
using collector_type = osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false>;
|
||||
|
||||
using assembler_config_type = typename TAssembler::config_type;
|
||||
const assembler_config_type m_assembler_config;
|
||||
|
||||
osmium::memory::Buffer m_output_buffer;
|
||||
|
||||
area_stats m_stats;
|
||||
|
||||
static constexpr size_t initial_output_buffer_size = 1024 * 1024;
|
||||
static constexpr size_t max_buffer_size_for_flush = 100 * 1024;
|
||||
|
||||
void flush_output_buffer() {
|
||||
if (this->callback()) {
|
||||
osmium::memory::Buffer buffer{initial_output_buffer_size};
|
||||
using std::swap;
|
||||
swap(buffer, m_output_buffer);
|
||||
this->callback()(std::move(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
void possibly_flush_output_buffer() {
|
||||
if (m_output_buffer.committed() > max_buffer_size_for_flush) {
|
||||
flush_output_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit MultipolygonCollector(const assembler_config_type& assembler_config) :
|
||||
collector_type(),
|
||||
m_assembler_config(assembler_config),
|
||||
m_output_buffer(initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes) {
|
||||
}
|
||||
|
||||
const area_stats& stats() const noexcept {
|
||||
return m_stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* We are interested in all relations tagged with type=multipolygon
|
||||
* or type=boundary.
|
||||
*
|
||||
* Overwritten from the base class.
|
||||
*/
|
||||
bool keep_relation(const osmium::Relation& relation) const {
|
||||
const char* type = relation.tags().get_value_by_key("type");
|
||||
|
||||
// ignore relations without "type" tag
|
||||
if (!type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwritten from the base class.
|
||||
*/
|
||||
bool keep_member(const osmium::relations::RelationMeta& /*relation_meta*/, const osmium::RelationMember& member) const {
|
||||
// We are only interested in members of type way.
|
||||
return member.type() == osmium::item_type::way;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when a way is not in any multipolygon
|
||||
* relation.
|
||||
*
|
||||
* Overwritten from the base class.
|
||||
*/
|
||||
void way_not_in_any_relation(const osmium::Way& way) {
|
||||
// you need at least 4 nodes to make up a polygon
|
||||
if (way.nodes().size() <= 3) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!way.nodes().front().location() || !way.nodes().back().location()) {
|
||||
throw osmium::invalid_location{"invalid location"};
|
||||
}
|
||||
if (way.ends_have_same_location()) {
|
||||
// way is closed and has enough nodes, build simple multipolygon
|
||||
TAssembler assembler{m_assembler_config};
|
||||
assembler(way, m_output_buffer);
|
||||
m_stats += assembler.stats();
|
||||
possibly_flush_output_buffer();
|
||||
}
|
||||
} catch (const osmium::invalid_location&) {
|
||||
// XXX ignore
|
||||
}
|
||||
}
|
||||
|
||||
void complete_relation(osmium::relations::RelationMeta& relation_meta) {
|
||||
const osmium::Relation& relation = this->get_relation(relation_meta);
|
||||
const osmium::memory::Buffer& buffer = this->members_buffer();
|
||||
|
||||
std::vector<const osmium::Way*> ways;
|
||||
ways.reserve(relation.members().size());
|
||||
for (const auto& member : relation.members()) {
|
||||
if (member.ref() != 0) {
|
||||
const size_t offset = this->get_offset(member.type(), member.ref());
|
||||
ways.push_back(&buffer.get<const osmium::Way>(offset));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
TAssembler assembler{m_assembler_config};
|
||||
assembler(relation, ways, m_output_buffer);
|
||||
m_stats += assembler.stats();
|
||||
possibly_flush_output_buffer();
|
||||
} catch (const osmium::invalid_location&) {
|
||||
// XXX ignore
|
||||
}
|
||||
}
|
||||
|
||||
void flush() {
|
||||
flush_output_buffer();
|
||||
}
|
||||
|
||||
osmium::memory::Buffer read() {
|
||||
osmium::memory::Buffer buffer{initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes};
|
||||
|
||||
using std::swap;
|
||||
swap(buffer, m_output_buffer);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}; // class MultipolygonCollector
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_MULTIPOLYGON_COLLECTOR_HPP
|
||||
@@ -0,0 +1,189 @@
|
||||
#ifndef OSMIUM_AREA_MULTIPOLYGON_MANAGER_HPP
|
||||
#define OSMIUM_AREA_MULTIPOLYGON_MANAGER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/area/stats.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/relations/manager_util.hpp>
|
||||
#include <osmium/relations/members_database.hpp>
|
||||
#include <osmium/relations/relations_database.hpp>
|
||||
#include <osmium/relations/relations_manager.hpp>
|
||||
#include <osmium/storage/item_stash.hpp>
|
||||
#include <osmium/tags/taglist.hpp>
|
||||
#include <osmium/tags/tags_filter.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* @brief Code related to the building of areas (multipolygons) from relations.
|
||||
*/
|
||||
namespace area {
|
||||
|
||||
/**
|
||||
* This class collects all data needed for creating areas from
|
||||
* relations tagged with type=multipolygon or type=boundary.
|
||||
* Most of its functionality is derived from the parent class
|
||||
* osmium::relations::Collector.
|
||||
*
|
||||
* The actual assembling of the areas is done by the assembler
|
||||
* class given as template argument.
|
||||
*
|
||||
* @tparam TAssembler Multipolygon Assembler class.
|
||||
* @pre The Ids of all objects must be unique in the input data.
|
||||
*/
|
||||
template <typename TAssembler>
|
||||
class MultipolygonManager : public osmium::relations::RelationsManager<MultipolygonManager<TAssembler>, false, true, false> {
|
||||
|
||||
using assembler_config_type = typename TAssembler::config_type;
|
||||
const assembler_config_type m_assembler_config;
|
||||
|
||||
area_stats m_stats;
|
||||
|
||||
osmium::TagsFilter m_filter;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a MultipolygonManager.
|
||||
*
|
||||
* @param assembler_config The configuration that will be given to
|
||||
* any newly constructed area assembler.
|
||||
* @param filter An optional filter specifying what tags are
|
||||
* needed on closed ways or multipolygon relations
|
||||
* to build the area.
|
||||
*/
|
||||
explicit MultipolygonManager(const assembler_config_type& assembler_config, const osmium::TagsFilter& filter = osmium::TagsFilter{true}) :
|
||||
m_assembler_config(assembler_config),
|
||||
m_filter(filter) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the aggregated statistics generated by the assemblers
|
||||
* called from the manager.
|
||||
*/
|
||||
const area_stats& stats() const noexcept {
|
||||
return m_stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* We are interested in all relations tagged with type=multipolygon
|
||||
* or type=boundary with at least one way member.
|
||||
*/
|
||||
bool new_relation(const osmium::Relation& relation) const {
|
||||
const char* type = relation.tags().get_value_by_key("type");
|
||||
|
||||
// ignore relations without "type" tag
|
||||
if (type == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (((!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"))) && osmium::tags::match_any_of(relation.tags(), m_filter)) {
|
||||
return std::any_of(relation.members().cbegin(), relation.members().cend(), [](const RelationMember& member) {
|
||||
return member.type() == osmium::item_type::way;
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when a relation is complete, ie. all members
|
||||
* were found in the input. It will build the area using the
|
||||
* assembler.
|
||||
*/
|
||||
void complete_relation(const osmium::Relation& relation) {
|
||||
std::vector<const osmium::Way*> ways;
|
||||
ways.reserve(relation.members().size());
|
||||
for (const auto& member : relation.members()) {
|
||||
if (member.ref() != 0) {
|
||||
ways.push_back(this->get_member_way(member.ref()));
|
||||
assert(ways.back() != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
TAssembler assembler{m_assembler_config};
|
||||
assembler(relation, ways, this->buffer());
|
||||
m_stats += assembler.stats();
|
||||
} catch (const osmium::invalid_location&) {
|
||||
// XXX ignore
|
||||
}
|
||||
}
|
||||
|
||||
void after_way(const osmium::Way& way) {
|
||||
// you need at least 4 nodes to make up a polygon
|
||||
if (way.nodes().size() <= 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!way.nodes().front().location() || !way.nodes().back().location()) {
|
||||
throw osmium::invalid_location{"invalid location"};
|
||||
}
|
||||
if (way.ends_have_same_location()) {
|
||||
if (way.tags().has_tag("area", "no")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (osmium::tags::match_none_of(way.tags(), m_filter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TAssembler assembler{m_assembler_config};
|
||||
assembler(way, this->buffer());
|
||||
m_stats += assembler.stats();
|
||||
this->possibly_flush();
|
||||
}
|
||||
} catch (const osmium::invalid_location&) {
|
||||
// XXX ignore
|
||||
}
|
||||
}
|
||||
|
||||
}; // class MultipolygonManager
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_MULTIPOLYGON_MANAGER_HPP
|
||||
@@ -0,0 +1,180 @@
|
||||
#ifndef OSMIUM_AREA_MULTIPOLYGON_MANAGER_LEGACY_HPP
|
||||
#define OSMIUM_AREA_MULTIPOLYGON_MANAGER_LEGACY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/area/stats.hpp>
|
||||
#include <osmium/handler.hpp>
|
||||
#include <osmium/handler/check_order.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/memory/callback_buffer.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/relations/manager_util.hpp>
|
||||
#include <osmium/relations/members_database.hpp>
|
||||
#include <osmium/relations/relations_database.hpp>
|
||||
#include <osmium/relations/relations_manager.hpp>
|
||||
#include <osmium/storage/item_stash.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* @brief Code related to the building of areas (multipolygons) from relations.
|
||||
*/
|
||||
namespace area {
|
||||
|
||||
/**
|
||||
* This class collects all data needed for creating areas from
|
||||
* relations tagged with type=multipolygon or type=boundary.
|
||||
* Most of its functionality is derived from the parent class
|
||||
* osmium::relations::Collector.
|
||||
*
|
||||
* The actual assembling of the areas is done by the assembler
|
||||
* class given as template argument.
|
||||
*
|
||||
* @tparam TAssembler Multipolygon Assembler class.
|
||||
* @pre The Ids of all objects must be unique in the input data.
|
||||
*/
|
||||
template <typename TAssembler>
|
||||
class MultipolygonManagerLegacy : public osmium::relations::RelationsManager<MultipolygonManagerLegacy<TAssembler>, false, true, false> {
|
||||
|
||||
using assembler_config_type = typename TAssembler::config_type;
|
||||
const assembler_config_type m_assembler_config;
|
||||
|
||||
area_stats m_stats;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a MultipolygonManagerLegacy.
|
||||
*
|
||||
* @param assembler_config The configuration that will be given to
|
||||
* any newly constructed area assembler.
|
||||
*/
|
||||
explicit MultipolygonManagerLegacy(const assembler_config_type& assembler_config) :
|
||||
m_assembler_config(assembler_config) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the aggregated statistics generated by the assemblers
|
||||
* called from the manager.
|
||||
*/
|
||||
const area_stats& stats() const noexcept {
|
||||
return m_stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* We are interested in all relations tagged with type=multipolygon
|
||||
* or type=boundary with at least one way member.
|
||||
*/
|
||||
bool new_relation(const osmium::Relation& relation) const {
|
||||
const char* type = relation.tags().get_value_by_key("type");
|
||||
|
||||
// ignore relations without "type" tag
|
||||
if (!type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"))) {
|
||||
return std::any_of(relation.members().cbegin(), relation.members().cend(), [](const RelationMember& member) {
|
||||
return member.type() == osmium::item_type::way;
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when a relation is complete, ie. all members
|
||||
* were found in the input. It will build the area using the
|
||||
* assembler.
|
||||
*/
|
||||
void complete_relation(const osmium::Relation& relation) {
|
||||
std::vector<const osmium::Way*> ways;
|
||||
ways.reserve(relation.members().size());
|
||||
for (const auto& member : relation.members()) {
|
||||
if (member.ref() != 0) {
|
||||
ways.push_back(this->get_member_way(member.ref()));
|
||||
assert(ways.back() != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
TAssembler assembler{m_assembler_config};
|
||||
assembler(relation, ways, this->buffer());
|
||||
m_stats += assembler.stats();
|
||||
} catch (const osmium::invalid_location&) {
|
||||
// XXX ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when a way is not in any multipolygon
|
||||
* relation.
|
||||
*/
|
||||
void way_not_in_any_relation(const osmium::Way& way) {
|
||||
// you need at least 4 nodes to make up a polygon
|
||||
if (way.nodes().size() <= 3) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!way.nodes().front().location() || !way.nodes().back().location()) {
|
||||
throw osmium::invalid_location{"invalid location"};
|
||||
}
|
||||
if (way.ends_have_same_location()) {
|
||||
// way is closed and has enough nodes, build simple multipolygon
|
||||
TAssembler assembler{m_assembler_config};
|
||||
assembler(way, this->buffer());
|
||||
m_stats += assembler.stats();
|
||||
}
|
||||
} catch (const osmium::invalid_location&) {
|
||||
// XXX ignore
|
||||
}
|
||||
}
|
||||
|
||||
}; // class MultipolygonManagerLegacy
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_MULTIPOLYGON_MANAGER_LEGACY_HPP
|
||||
@@ -0,0 +1,238 @@
|
||||
#ifndef OSMIUM_AREA_PROBLEM_REPORTER_HPP
|
||||
#define OSMIUM_AREA_PROBLEM_REPORTER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
class NodeRef;
|
||||
class Way;
|
||||
|
||||
namespace area {
|
||||
|
||||
/**
|
||||
* When assembling a multipolygon/area from a multipolygon relation
|
||||
* or a closed way several problems can be detected. This includes
|
||||
* intersections between lines, wrong role attributes on relation
|
||||
* members etc. These problems are reported by the area::Assembler
|
||||
* class to the ProblemReporter class or one of its child classes.
|
||||
*
|
||||
* This is the parent class which does nothing with the reports.
|
||||
* Child classes are expected to implement different ways of
|
||||
* reporting the problems.
|
||||
*/
|
||||
class ProblemReporter {
|
||||
|
||||
protected:
|
||||
|
||||
// Type of object we are currently working on
|
||||
osmium::item_type m_object_type;
|
||||
|
||||
// ID of the relation/way we are currently working on
|
||||
osmium::object_id_type m_object_id;
|
||||
|
||||
// Number of nodes in the area
|
||||
size_t m_nodes;
|
||||
|
||||
public:
|
||||
|
||||
ProblemReporter() = default;
|
||||
|
||||
virtual ~ProblemReporter() = default;
|
||||
|
||||
/**
|
||||
* Set the object the next problem reports will be on.
|
||||
*
|
||||
* @param object_type The type of the object.
|
||||
* @param object_id The ID of the object.
|
||||
*/
|
||||
void set_object(osmium::item_type object_type, osmium::object_id_type object_id) noexcept {
|
||||
m_object_type = object_type;
|
||||
m_object_id = object_id;
|
||||
}
|
||||
|
||||
void set_nodes(size_t nodes) noexcept {
|
||||
m_nodes = nodes;
|
||||
}
|
||||
|
||||
// Disable "unused-parameter" warning, so that the compiler will not complain.
|
||||
// We can't remove the parameter names, because then doxygen will complain.
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
/**
|
||||
* Report a duplicate node, ie. two nodes with the same location.
|
||||
*
|
||||
* @param node_id1 ID of the first node.
|
||||
* @param node_id2 ID of the second node.
|
||||
* @param location Location of both nodes.
|
||||
*/
|
||||
virtual void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a node/location where rings touch. This is often wrong,
|
||||
* but not necessarily so.
|
||||
*
|
||||
* @param node_id ID of the node.
|
||||
* @param location Location of the node.
|
||||
*/
|
||||
virtual void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report an intersection between two segments.
|
||||
*
|
||||
* @param way1_id ID of the first involved way.
|
||||
* @param way1_seg_start Location where the segment of the first way with the intersection starts
|
||||
* @param way1_seg_end Location where the segment of the first way with the intersection ends
|
||||
* @param way2_id ID of the second involved way.
|
||||
* @param way2_seg_start Location where the segment of the second way with the intersection starts
|
||||
* @param way2_seg_end Location where the segment of the second way with the intersection ends
|
||||
* @param intersection Location of the intersection. This might be slightly off the correct location due to rounding.
|
||||
*/
|
||||
virtual void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
|
||||
osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a duplicate segments. Two or more segments are directly
|
||||
* on top of each other. This can be a problem, if there is a
|
||||
* spike for instance, or it could be okay, if there are touching
|
||||
* inner rings.
|
||||
*
|
||||
* @param nr1 NodeRef of one end of the segment.
|
||||
* @param nr2 NodeRef of the other end of the segment.
|
||||
*/
|
||||
virtual void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a duplicate segments. Two or more segments are directly
|
||||
* on top of each other. This can be a problem, if there is a
|
||||
* spike for instance, or it could be okay, if there are touching
|
||||
* inner rings.
|
||||
*
|
||||
* @param nr1 NodeRef of one end of the segment.
|
||||
* @param nr2 NodeRef of the other end of the segment.
|
||||
*/
|
||||
virtual void report_overlapping_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report an open ring.
|
||||
*
|
||||
* @param nr NodeRef of one end of the ring.
|
||||
* @param way Optional pointer to way the end node is in.
|
||||
*/
|
||||
virtual void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a segment that should have role "outer", but has a different role.
|
||||
*
|
||||
* @param way_id ID of the way this segment is in.
|
||||
* @param seg_start Start of the segment with the wrong role.
|
||||
* @param seg_end End of the segment with the wrong role.
|
||||
*/
|
||||
virtual void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a segment that should have role "inner", but has a different role.
|
||||
*
|
||||
* @param way_id ID of the way this segment is in.
|
||||
* @param seg_start Start of the segment with the wrong role.
|
||||
* @param seg_end End of the segment with the wrong role.
|
||||
*/
|
||||
virtual void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a way that is in multiple rings.
|
||||
*
|
||||
* @param way The way.
|
||||
*/
|
||||
virtual void report_way_in_multiple_rings(const osmium::Way& way) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a way with role inner that has the same tags as the
|
||||
* relation or outer ways.
|
||||
*
|
||||
* @param way The way.
|
||||
*/
|
||||
virtual void report_inner_with_same_tags(const osmium::Way& way) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report an invalid location in a way.
|
||||
*
|
||||
* @param way_id ID of the way the node is in.
|
||||
* @param node_id ID of the node with the invalid location.
|
||||
*/
|
||||
virtual void report_invalid_location(osmium::object_id_type way_id, osmium::object_id_type node_id) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a way that is more than once in a relation.
|
||||
*
|
||||
* @param way The way
|
||||
*/
|
||||
virtual void report_duplicate_way(const osmium::Way& way) {
|
||||
}
|
||||
|
||||
/**
|
||||
* In addition to reporting specific problems, this is used to
|
||||
* report all ways belonging to areas having problems.
|
||||
*
|
||||
* @param way The way
|
||||
*/
|
||||
virtual void report_way(const osmium::Way& way) {
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
}; // class ProblemReporter
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_PROBLEM_REPORTER_HPP
|
||||
@@ -0,0 +1,141 @@
|
||||
#ifndef OSMIUM_AREA_PROBLEM_REPORTER_EXCEPTION_HPP
|
||||
#define OSMIUM_AREA_PROBLEM_REPORTER_EXCEPTION_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <osmium/area/problem_reporter_stream.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
class NodeRef;
|
||||
class Way;
|
||||
|
||||
namespace area {
|
||||
|
||||
class ProblemReporterException : public ProblemReporterStream {
|
||||
|
||||
std::stringstream m_sstream;
|
||||
|
||||
public:
|
||||
|
||||
ProblemReporterException() :
|
||||
ProblemReporterStream(m_sstream) {
|
||||
}
|
||||
|
||||
~ProblemReporterException() override = default;
|
||||
|
||||
void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override {
|
||||
m_sstream.str("");
|
||||
ProblemReporterStream::report_duplicate_node(node_id1, node_id2, location);
|
||||
throw std::runtime_error{m_sstream.str()};
|
||||
}
|
||||
|
||||
void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override {
|
||||
m_sstream.str("");
|
||||
ProblemReporterStream::report_touching_ring(node_id, location);
|
||||
throw std::runtime_error{m_sstream.str()};
|
||||
}
|
||||
|
||||
void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
|
||||
osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override {
|
||||
m_sstream.str("");
|
||||
ProblemReporterStream::report_intersection(way1_id, way1_seg_start, way1_seg_end, way2_id, way2_seg_start, way2_seg_end, intersection);
|
||||
throw std::runtime_error{m_sstream.str()};
|
||||
}
|
||||
|
||||
void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
|
||||
m_sstream.str("");
|
||||
ProblemReporterStream::report_duplicate_segment(nr1, nr2);
|
||||
throw std::runtime_error{m_sstream.str()};
|
||||
}
|
||||
|
||||
void report_overlapping_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
|
||||
m_sstream.str("");
|
||||
ProblemReporterStream::report_overlapping_segment(nr1, nr2);
|
||||
throw std::runtime_error{m_sstream.str()};
|
||||
}
|
||||
|
||||
void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override {
|
||||
m_sstream.str("");
|
||||
ProblemReporterStream::report_ring_not_closed(nr, way);
|
||||
throw std::runtime_error{m_sstream.str()};
|
||||
}
|
||||
|
||||
void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
|
||||
m_sstream.str("");
|
||||
ProblemReporterStream::report_role_should_be_outer(way_id, seg_start, seg_end);
|
||||
throw std::runtime_error{m_sstream.str()};
|
||||
}
|
||||
|
||||
void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
|
||||
m_sstream.str("");
|
||||
ProblemReporterStream::report_role_should_be_inner(way_id, seg_start, seg_end);
|
||||
throw std::runtime_error{m_sstream.str()};
|
||||
}
|
||||
|
||||
void report_way_in_multiple_rings(const osmium::Way& way) override {
|
||||
m_sstream.str("");
|
||||
ProblemReporterStream::report_way_in_multiple_rings(way);
|
||||
throw std::runtime_error{m_sstream.str()};
|
||||
}
|
||||
|
||||
void report_inner_with_same_tags(const osmium::Way& way) override {
|
||||
m_sstream.str("");
|
||||
ProblemReporterStream::report_inner_with_same_tags(way);
|
||||
throw std::runtime_error{m_sstream.str()};
|
||||
}
|
||||
|
||||
void report_invalid_location(osmium::object_id_type way_id, osmium::object_id_type node_id) override {
|
||||
m_sstream.str("");
|
||||
ProblemReporterStream::report_invalid_location(way_id, node_id);
|
||||
throw std::runtime_error{m_sstream.str()};
|
||||
}
|
||||
|
||||
void report_duplicate_way(const osmium::Way& way) override {
|
||||
m_sstream.str("");
|
||||
ProblemReporterStream::report_duplicate_way(way);
|
||||
throw std::runtime_error{m_sstream.str()};
|
||||
}
|
||||
|
||||
}; // class ProblemReporterException
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_PROBLEM_REPORTER_EXCEPTION_HPP
|
||||
@@ -0,0 +1,250 @@
|
||||
#ifndef OSMIUM_AREA_PROBLEM_REPORTER_OGR_HPP
|
||||
#define OSMIUM_AREA_PROBLEM_REPORTER_OGR_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* This file contains code for reporting problems through OGR when
|
||||
* assembling multipolygons.
|
||||
*
|
||||
* @attention If you include this file, you'll need to link with `libgdal`.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <gdalcpp.hpp>
|
||||
|
||||
#include <osmium/area/problem_reporter.hpp>
|
||||
#include <osmium/geom/factory.hpp>
|
||||
#include <osmium/geom/ogr.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/node_ref_list.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace area {
|
||||
|
||||
/**
|
||||
* Report problems when assembling areas by adding them to
|
||||
* layers in an OGR datasource.
|
||||
*/
|
||||
class ProblemReporterOGR : public ProblemReporter {
|
||||
|
||||
osmium::geom::OGRFactory<> m_ogr_factory;
|
||||
|
||||
gdalcpp::Layer m_layer_perror;
|
||||
gdalcpp::Layer m_layer_lerror;
|
||||
gdalcpp::Layer m_layer_ways;
|
||||
|
||||
void set_object(gdalcpp::Feature& feature) {
|
||||
const char t[2] = {osmium::item_type_to_char(m_object_type), '\0'};
|
||||
feature.set_field("obj_type", t);
|
||||
feature.set_field("obj_id", int32_t(m_object_id));
|
||||
feature.set_field("nodes", int32_t(m_nodes));
|
||||
}
|
||||
|
||||
void write_point(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location location) {
|
||||
gdalcpp::Feature feature{m_layer_perror, m_ogr_factory.create_point(location)};
|
||||
set_object(feature);
|
||||
feature.set_field("id1", double(id1));
|
||||
feature.set_field("id2", double(id2));
|
||||
feature.set_field("problem", problem_type);
|
||||
feature.add_to_layer();
|
||||
}
|
||||
|
||||
void write_line(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location loc1, osmium::Location loc2) {
|
||||
auto ogr_linestring = std::unique_ptr<OGRLineString>{new OGRLineString{}};
|
||||
ogr_linestring->addPoint(loc1.lon(), loc1.lat());
|
||||
ogr_linestring->addPoint(loc2.lon(), loc2.lat());
|
||||
|
||||
gdalcpp::Feature feature{m_layer_lerror, std::move(ogr_linestring)};
|
||||
set_object(feature);
|
||||
feature.set_field("id1", static_cast<double>(id1));
|
||||
feature.set_field("id2", static_cast<double>(id2));
|
||||
feature.set_field("problem", problem_type);
|
||||
feature.add_to_layer();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit ProblemReporterOGR(gdalcpp::Dataset& dataset) :
|
||||
m_layer_perror(dataset, "perrors", wkbPoint),
|
||||
m_layer_lerror(dataset, "lerrors", wkbLineString),
|
||||
m_layer_ways(dataset, "ways", wkbLineString) {
|
||||
|
||||
// 64bit integers are not supported in GDAL < 2, so we
|
||||
// are using a workaround here in fields where we expect
|
||||
// node IDs, we use real numbers.
|
||||
m_layer_perror
|
||||
.add_field("obj_type", OFTString, 1)
|
||||
.add_field("obj_id", OFTInteger, 10)
|
||||
.add_field("nodes", OFTInteger, 8)
|
||||
.add_field("id1", OFTReal, 12, 1)
|
||||
.add_field("id2", OFTReal, 12, 1)
|
||||
.add_field("problem", OFTString, 30)
|
||||
;
|
||||
|
||||
m_layer_lerror
|
||||
.add_field("obj_type", OFTString, 1)
|
||||
.add_field("obj_id", OFTInteger, 10)
|
||||
.add_field("nodes", OFTInteger, 8)
|
||||
.add_field("id1", OFTReal, 12, 1)
|
||||
.add_field("id2", OFTReal, 12, 1)
|
||||
.add_field("problem", OFTString, 30)
|
||||
;
|
||||
|
||||
m_layer_ways
|
||||
.add_field("obj_type", OFTString, 1)
|
||||
.add_field("obj_id", OFTInteger, 10)
|
||||
.add_field("way_id", OFTInteger, 10)
|
||||
.add_field("nodes", OFTInteger, 8)
|
||||
;
|
||||
}
|
||||
|
||||
~ProblemReporterOGR() override = default;
|
||||
|
||||
void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override {
|
||||
write_point("duplicate_node", node_id1, node_id2, location);
|
||||
}
|
||||
|
||||
void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override {
|
||||
write_point("touching_ring", node_id, 0, location);
|
||||
}
|
||||
|
||||
void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
|
||||
osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override {
|
||||
write_point("intersection", way1_id, way2_id, intersection);
|
||||
write_line("intersection", way1_id, way2_id, way1_seg_start, way1_seg_end);
|
||||
write_line("intersection", way2_id, way1_id, way2_seg_start, way2_seg_end);
|
||||
}
|
||||
|
||||
void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
|
||||
write_line("duplicate_segment", nr1.ref(), nr2.ref(), nr1.location(), nr2.location());
|
||||
}
|
||||
|
||||
void report_overlapping_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
|
||||
write_line("overlapping_segment", nr1.ref(), nr2.ref(), nr1.location(), nr2.location());
|
||||
}
|
||||
|
||||
void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override {
|
||||
write_point("ring_not_closed", nr.ref(), way ? way->id() : 0, nr.location());
|
||||
}
|
||||
|
||||
void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
|
||||
write_line("role_should_be_outer", way_id, 0, seg_start, seg_end);
|
||||
}
|
||||
|
||||
void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
|
||||
write_line("role_should_be_inner", way_id, 0, seg_start, seg_end);
|
||||
}
|
||||
|
||||
void report_way_in_multiple_rings(const osmium::Way& way) override {
|
||||
if (way.nodes().size() < 2) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
gdalcpp::Feature feature{m_layer_lerror, m_ogr_factory.create_linestring(way)};
|
||||
set_object(feature);
|
||||
feature.set_field("id1", int32_t(way.id()));
|
||||
feature.set_field("id2", 0);
|
||||
feature.set_field("problem", "way_in_multiple_rings");
|
||||
feature.add_to_layer();
|
||||
} catch (const osmium::geometry_error&) {
|
||||
// XXX
|
||||
}
|
||||
}
|
||||
|
||||
void report_inner_with_same_tags(const osmium::Way& way) override {
|
||||
if (way.nodes().size() < 2) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
gdalcpp::Feature feature{m_layer_lerror, m_ogr_factory.create_linestring(way)};
|
||||
set_object(feature);
|
||||
feature.set_field("id1", int32_t(way.id()));
|
||||
feature.set_field("id2", 0);
|
||||
feature.set_field("problem", "inner_with_same_tags");
|
||||
feature.add_to_layer();
|
||||
} catch (const osmium::geometry_error&) {
|
||||
// XXX
|
||||
}
|
||||
}
|
||||
|
||||
void report_duplicate_way(const osmium::Way& way) override {
|
||||
if (way.nodes().size() < 2) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
gdalcpp::Feature feature{m_layer_lerror, m_ogr_factory.create_linestring(way)};
|
||||
set_object(feature);
|
||||
feature.set_field("id1", int32_t(way.id()));
|
||||
feature.set_field("id2", 0);
|
||||
feature.set_field("problem", "duplicate_way");
|
||||
feature.add_to_layer();
|
||||
} catch (const osmium::geometry_error&) {
|
||||
// XXX
|
||||
}
|
||||
}
|
||||
|
||||
void report_way(const osmium::Way& way) override {
|
||||
if (way.nodes().empty()) {
|
||||
return;
|
||||
}
|
||||
if (way.nodes().size() == 1) {
|
||||
const auto& first_nr = way.nodes()[0];
|
||||
write_point("single_node_in_way", way.id(), first_nr.ref(), first_nr.location());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
gdalcpp::Feature feature{m_layer_ways, m_ogr_factory.create_linestring(way)};
|
||||
set_object(feature);
|
||||
feature.set_field("way_id", int32_t(way.id()));
|
||||
feature.add_to_layer();
|
||||
} catch (const osmium::geometry_error&) {
|
||||
// XXX
|
||||
}
|
||||
}
|
||||
|
||||
}; // class ProblemReporterOGR
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_PROBLEM_REPORTER_OGR_HPP
|
||||
@@ -0,0 +1,139 @@
|
||||
#ifndef OSMIUM_AREA_PROBLEM_REPORTER_STREAM_HPP
|
||||
#define OSMIUM_AREA_PROBLEM_REPORTER_STREAM_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include <osmium/area/problem_reporter.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace area {
|
||||
|
||||
class ProblemReporterStream : public ProblemReporter {
|
||||
|
||||
std::ostream* m_out;
|
||||
|
||||
public:
|
||||
|
||||
explicit ProblemReporterStream(std::ostream& out) :
|
||||
m_out(&out) {
|
||||
}
|
||||
|
||||
~ProblemReporterStream() override = default;
|
||||
|
||||
void header(const char* msg) {
|
||||
*m_out << "DATA PROBLEM: " << msg << " on " << item_type_to_char(m_object_type) << m_object_id << " (with " << m_nodes << " nodes): ";
|
||||
}
|
||||
|
||||
void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override {
|
||||
header("duplicate node");
|
||||
*m_out << "node_id1=" << node_id1 << " node_id2=" << node_id2 << " location=" << location << "\n";
|
||||
}
|
||||
|
||||
void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override {
|
||||
header("touching ring");
|
||||
*m_out << "node_id=" << node_id << " location=" << location << "\n";
|
||||
}
|
||||
|
||||
void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
|
||||
osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override {
|
||||
header("intersection");
|
||||
*m_out << "way1_id=" << way1_id << " way1_seg_start=" << way1_seg_start << " way1_seg_end=" << way1_seg_end
|
||||
<< " way2_id=" << way2_id << " way2_seg_start=" << way2_seg_start << " way2_seg_end=" << way2_seg_end << " intersection=" << intersection << "\n";
|
||||
}
|
||||
|
||||
void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
|
||||
header("duplicate segment");
|
||||
*m_out << "node_id1=" << nr1.ref() << " location1=" << nr1.location()
|
||||
<< " node_id2=" << nr2.ref() << " location2=" << nr2.location() << "\n";
|
||||
}
|
||||
|
||||
void report_overlapping_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
|
||||
header("overlapping segment");
|
||||
*m_out << "node_id1=" << nr1.ref() << " location1=" << nr1.location()
|
||||
<< " node_id2=" << nr2.ref() << " location2=" << nr2.location() << "\n";
|
||||
}
|
||||
|
||||
void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override {
|
||||
header("ring not closed");
|
||||
*m_out << "node_id=" << nr.ref() << " location=" << nr.location();
|
||||
if (way) {
|
||||
*m_out << " on way " << way->id();
|
||||
}
|
||||
*m_out << "\n";
|
||||
}
|
||||
|
||||
void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
|
||||
header("role should be outer");
|
||||
*m_out << "way_id=" << way_id << " seg_start=" << seg_start << " seg_end=" << seg_end << "\n";
|
||||
}
|
||||
|
||||
void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
|
||||
header("role should be inner");
|
||||
*m_out << "way_id=" << way_id << " seg_start=" << seg_start << " seg_end=" << seg_end << "\n";
|
||||
}
|
||||
|
||||
void report_way_in_multiple_rings(const osmium::Way& way) override {
|
||||
header("way in multiple rings");
|
||||
*m_out << "way_id=" << way.id() << '\n';
|
||||
}
|
||||
|
||||
void report_inner_with_same_tags(const osmium::Way& way) override {
|
||||
header("inner way with same tags as relation or outer");
|
||||
*m_out << "way_id=" << way.id() << '\n';
|
||||
}
|
||||
|
||||
void report_invalid_location(osmium::object_id_type way_id, osmium::object_id_type node_id) override {
|
||||
header("invalid location");
|
||||
*m_out << "way_id=" << way_id << " node_id=" << node_id << '\n';
|
||||
}
|
||||
|
||||
void report_duplicate_way(const osmium::Way& way) override {
|
||||
header("duplicate way");
|
||||
*m_out << "way_id=" << way.id() << '\n';
|
||||
}
|
||||
|
||||
}; // class ProblemReporterStream
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_PROBLEM_REPORTER_STREAM_HPP
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
#ifndef OSMIUM_AREA_STATS_HPP
|
||||
#define OSMIUM_AREA_STATS_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace area {
|
||||
|
||||
/**
|
||||
* These statistics are generated by the area assembler code. They
|
||||
* tell the user of the assembler a lot about the objects this area
|
||||
* is made out of, what happened during the assembly, and what errors
|
||||
* there were.
|
||||
*/
|
||||
struct area_stats {
|
||||
uint64_t area_really_complex_case = 0; ///< Most difficult case with rings touching in multiple points
|
||||
uint64_t area_simple_case = 0; ///< Simple case, no touching rings
|
||||
uint64_t area_touching_rings_case = 0; ///< More difficult case with touching rings
|
||||
uint64_t duplicate_nodes = 0; ///< Consecutive identical nodes or consecutive nodes with same location
|
||||
uint64_t duplicate_segments = 0; ///< Segments duplicated (going back and forth)
|
||||
uint64_t duplicate_ways = 0; ///< Ways that are in relation more than once
|
||||
uint64_t from_relations = 0; ///< Area created from multipolygon relation
|
||||
uint64_t from_ways = 0; ///< Area created from way
|
||||
uint64_t inner_rings = 0; ///< Number of inner rings
|
||||
uint64_t inner_with_same_tags = 0; ///< Number of inner ways with same tags as area
|
||||
uint64_t intersections = 0; ///< Number of intersections between segments
|
||||
uint64_t member_ways = 0; ///< Number of ways in the area
|
||||
uint64_t no_tags_on_relation = 0; ///< No tags on relation (old-style multipolygon with tags on outer ways)
|
||||
uint64_t no_way_in_mp_relation = 0; ///< Multipolygon relation with no way members
|
||||
uint64_t nodes = 0; ///< Number of nodes in the area
|
||||
uint64_t open_rings = 0; ///< Number of open rings in the area
|
||||
uint64_t outer_rings = 0; ///< Number of outer rings in the area
|
||||
uint64_t overlapping_segments = 0; ///< Three or more segments with same end points
|
||||
uint64_t short_ways = 0; ///< Number of ways with less than two nodes
|
||||
uint64_t single_way_in_mp_relation = 0; ///< Multipolygon relation containing a single way
|
||||
uint64_t touching_rings = 0; ///< Rings touching in a node
|
||||
uint64_t ways_in_multiple_rings = 0; ///< Different segments of a way ended up in different rings
|
||||
uint64_t wrong_role = 0; ///< Member has wrong role (not "outer", "inner", or empty)
|
||||
uint64_t invalid_locations = 0; ///< Invalid location found
|
||||
|
||||
area_stats& operator+=(const area_stats& other) noexcept {
|
||||
area_really_complex_case += other.area_really_complex_case;
|
||||
area_simple_case += other.area_simple_case;
|
||||
area_touching_rings_case += other.area_touching_rings_case;
|
||||
duplicate_nodes += other.duplicate_nodes;
|
||||
duplicate_segments += other.duplicate_segments;
|
||||
duplicate_ways += other.duplicate_ways;
|
||||
from_relations += other.from_relations;
|
||||
from_ways += other.from_ways;
|
||||
inner_rings += other.inner_rings;
|
||||
inner_with_same_tags += other.inner_with_same_tags;
|
||||
intersections += other.intersections;
|
||||
member_ways += other.member_ways;
|
||||
no_tags_on_relation += other.no_tags_on_relation;
|
||||
no_way_in_mp_relation += other.no_way_in_mp_relation;
|
||||
nodes += other.nodes;
|
||||
open_rings += other.open_rings;
|
||||
outer_rings += other.outer_rings;
|
||||
short_ways += other.short_ways;
|
||||
single_way_in_mp_relation += other.single_way_in_mp_relation;
|
||||
touching_rings += other.touching_rings;
|
||||
ways_in_multiple_rings += other.ways_in_multiple_rings;
|
||||
wrong_role += other.wrong_role;
|
||||
invalid_locations += invalid_locations;
|
||||
return *this;
|
||||
}
|
||||
|
||||
}; // struct area_stats
|
||||
|
||||
template <typename TChar, typename TTraits>
|
||||
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const area_stats& s) {
|
||||
return out << " area_really_complex_case=" << s.area_really_complex_case
|
||||
<< " area_simple_case=" << s.area_simple_case
|
||||
<< " area_touching_rings_case=" << s.area_touching_rings_case
|
||||
<< " duplicate_nodes=" << s.duplicate_nodes
|
||||
<< " duplicate_segments=" << s.duplicate_segments
|
||||
<< " duplicate_ways=" << s.duplicate_ways
|
||||
<< " from_relations=" << s.from_relations
|
||||
<< " from_ways=" << s.from_ways
|
||||
<< " inner_rings=" << s.inner_rings
|
||||
<< " inner_with_same_tags=" << s.inner_with_same_tags
|
||||
<< " intersections=" << s.intersections
|
||||
<< " member_ways=" << s.member_ways
|
||||
<< " no_tags_on_relation=" << s.no_tags_on_relation
|
||||
<< " no_way_in_mp_relation=" << s.no_way_in_mp_relation
|
||||
<< " nodes=" << s.nodes
|
||||
<< " open_rings=" << s.open_rings
|
||||
<< " outer_rings=" << s.outer_rings
|
||||
<< " short_ways=" << s.short_ways
|
||||
<< " single_way_in_mp_relation=" << s.single_way_in_mp_relation
|
||||
<< " touching_rings=" << s.touching_rings
|
||||
<< " ways_in_multiple_rings=" << s.ways_in_multiple_rings
|
||||
<< " wrong_role=" << s.wrong_role
|
||||
<< " invalid_locations=" << s.invalid_locations;
|
||||
}
|
||||
|
||||
} // namespace area
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_AREA_STATS_HPP
|
||||
@@ -0,0 +1,922 @@
|
||||
#ifndef OSMIUM_BUILDER_ATTR_HPP
|
||||
#define OSMIUM_BUILDER_ATTR_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/builder/builder.hpp>
|
||||
#include <osmium/builder/osm_object_builder.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm/changeset.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace builder {
|
||||
|
||||
namespace detail {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// workaround for bug in MSVC
|
||||
|
||||
template <typename THandler, typename... TTypes>
|
||||
struct is_handled_by;
|
||||
|
||||
template <typename THandler>
|
||||
struct is_handled_by<THandler> {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
template <typename THandler, typename T, typename... TRest>
|
||||
struct is_handled_by<THandler, T, TRest...> {
|
||||
static constexpr bool value = std::is_base_of<typename T::handler, THandler>::value ||
|
||||
is_handled_by<THandler, TRest...>::value;
|
||||
};
|
||||
|
||||
template <typename THandler, typename... TTypes>
|
||||
struct are_all_handled_by;
|
||||
|
||||
template <typename THandler, typename T>
|
||||
struct are_all_handled_by<THandler, T> {
|
||||
static constexpr bool value = std::is_base_of<typename T::handler, THandler>::value;
|
||||
};
|
||||
|
||||
template <typename THandler, typename T, typename... TRest>
|
||||
struct are_all_handled_by<THandler, T, TRest...> {
|
||||
static constexpr bool value = std::is_base_of<typename T::handler, THandler>::value &&
|
||||
are_all_handled_by<THandler, TRest...>::value;
|
||||
};
|
||||
#else
|
||||
// True if Predicate matches for none of the types Ts
|
||||
template <template<typename> class Predicate, typename... Ts>
|
||||
struct static_none_of : std::is_same<std::tuple<std::false_type, typename Predicate<Ts>::type...>,
|
||||
std::tuple<typename Predicate<Ts>::type..., std::false_type>>
|
||||
{};
|
||||
|
||||
// True if Predicate matches for all of the types Ts
|
||||
template <template<typename> class Predicate, typename... Ts>
|
||||
struct static_all_of : std::is_same<std::tuple<std::true_type, typename Predicate<Ts>::type...>,
|
||||
std::tuple<typename Predicate<Ts>::type..., std::true_type>>
|
||||
{};
|
||||
|
||||
// True if THandler is derived from the handler for at least one of the types in TTypes
|
||||
template <typename THandler, typename... TTypes>
|
||||
struct is_handled_by {
|
||||
template <typename T>
|
||||
using HasHandler = std::is_base_of<typename T::handler, THandler>;
|
||||
|
||||
static constexpr bool value = !static_none_of<HasHandler, TTypes...>::value;
|
||||
};
|
||||
|
||||
// True if THandler is derived from the handlers of all the types in TTypes
|
||||
template <typename THandler, typename... TTypes>
|
||||
struct are_all_handled_by {
|
||||
template <typename T>
|
||||
using HasHandler = std::is_base_of<typename T::handler, THandler>;
|
||||
|
||||
static constexpr bool value = static_all_of<HasHandler, TTypes...>::value;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
// Wraps any type, so that we can derive from it
|
||||
template <typename TType>
|
||||
struct type_wrapper {
|
||||
|
||||
using type = TType;
|
||||
|
||||
TType value;
|
||||
|
||||
constexpr explicit type_wrapper(const TType& v) :
|
||||
value(v) {
|
||||
}
|
||||
|
||||
}; // struct type_wrapper
|
||||
|
||||
// Small wrapper for begin/end iterator
|
||||
template <typename TType>
|
||||
struct iterator_wrapper {
|
||||
|
||||
using type = TType;
|
||||
|
||||
TType first;
|
||||
TType last;
|
||||
|
||||
constexpr iterator_wrapper(TType begin, TType end) :
|
||||
first(begin),
|
||||
last(end) {}
|
||||
|
||||
constexpr TType begin() const {
|
||||
return first;
|
||||
}
|
||||
|
||||
constexpr TType end() const {
|
||||
return last;
|
||||
}
|
||||
|
||||
}; // struct iterator_wrapper
|
||||
|
||||
|
||||
struct entity_handler {};
|
||||
struct object_handler;
|
||||
struct node_handler;
|
||||
struct tags_handler;
|
||||
struct nodes_handler;
|
||||
struct members_handler;
|
||||
struct changeset_handler;
|
||||
struct discussion_handler;
|
||||
struct ring_handler;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#define OSMIUM_ATTRIBUTE(_handler, _name, _type) \
|
||||
struct _name : public osmium::builder::detail::type_wrapper<_type> { \
|
||||
using handler = osmium::builder::detail::_handler;
|
||||
|
||||
#define OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(_handler, _name, _type) \
|
||||
OSMIUM_ATTRIBUTE(_handler, _name, _type) \
|
||||
constexpr explicit _name(std::add_const<_type>::type& value) : \
|
||||
type_wrapper(value) {} \
|
||||
}
|
||||
|
||||
#define OSMIUM_ATTRIBUTE_ITER(_handler, _name) \
|
||||
template <typename TIterator> \
|
||||
struct _name : public osmium::builder::detail::iterator_wrapper<TIterator> { \
|
||||
using handler = osmium::builder::detail::_handler; \
|
||||
constexpr _name(TIterator first, TIterator last) : \
|
||||
osmium::builder::detail::iterator_wrapper<TIterator>(first, last) {} \
|
||||
}
|
||||
|
||||
namespace attr {
|
||||
|
||||
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(object_handler, _id, osmium::object_id_type);
|
||||
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(object_handler, _version, osmium::object_version_type);
|
||||
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(entity_handler, _uid, osmium::user_id_type);
|
||||
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(entity_handler, _cid, osmium::changeset_id_type);
|
||||
|
||||
OSMIUM_ATTRIBUTE(object_handler, _deleted, bool)
|
||||
constexpr explicit _deleted(bool value = true) noexcept :
|
||||
type_wrapper(value) {}
|
||||
};
|
||||
|
||||
OSMIUM_ATTRIBUTE(object_handler, _visible, bool)
|
||||
constexpr explicit _visible(bool value = true) noexcept :
|
||||
type_wrapper(value) {}
|
||||
};
|
||||
|
||||
OSMIUM_ATTRIBUTE(object_handler, _timestamp, osmium::Timestamp)
|
||||
constexpr explicit _timestamp(const osmium::Timestamp& value) noexcept :
|
||||
type_wrapper(value) {}
|
||||
constexpr explicit _timestamp(time_t value) noexcept :
|
||||
type_wrapper(osmium::Timestamp{value}) {}
|
||||
constexpr explicit _timestamp(uint32_t value) noexcept :
|
||||
type_wrapper(osmium::Timestamp{value}) {}
|
||||
explicit _timestamp(const char* value) :
|
||||
type_wrapper(osmium::Timestamp{value}) {}
|
||||
explicit _timestamp(const std::string& value) :
|
||||
type_wrapper(osmium::Timestamp{value}) {}
|
||||
};
|
||||
|
||||
OSMIUM_ATTRIBUTE(node_handler, _location, osmium::Location)
|
||||
constexpr explicit _location(const osmium::Location& value) noexcept :
|
||||
type_wrapper(value) {}
|
||||
explicit _location(double lat, double lon) :
|
||||
type_wrapper(osmium::Location{lat, lon}) {}
|
||||
};
|
||||
|
||||
OSMIUM_ATTRIBUTE(entity_handler, _user, const char*)
|
||||
constexpr explicit _user(const char* val) noexcept :
|
||||
type_wrapper(val) {}
|
||||
explicit _user(const std::string& val) noexcept :
|
||||
type_wrapper(val.c_str()) {}
|
||||
};
|
||||
|
||||
using pair_of_cstrings = std::pair<const char* const, const char* const>;
|
||||
using pair_of_strings = std::pair<const std::string&, const std::string&>;
|
||||
|
||||
class member_type {
|
||||
|
||||
osmium::item_type m_type;
|
||||
osmium::object_id_type m_ref;
|
||||
const char* m_role;
|
||||
|
||||
public:
|
||||
|
||||
constexpr member_type(osmium::item_type type, osmium::object_id_type ref, const char* role = "") noexcept :
|
||||
m_type(type),
|
||||
m_ref(ref),
|
||||
m_role(role) {
|
||||
}
|
||||
|
||||
constexpr osmium::item_type type() const noexcept {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
constexpr osmium::object_id_type ref() const noexcept {
|
||||
return m_ref;
|
||||
}
|
||||
|
||||
constexpr const char* role() const noexcept {
|
||||
return m_role;
|
||||
}
|
||||
|
||||
}; // class member_type
|
||||
|
||||
class member_type_string {
|
||||
|
||||
osmium::item_type m_type;
|
||||
osmium::object_id_type m_ref;
|
||||
std::string m_role;
|
||||
|
||||
public:
|
||||
|
||||
member_type_string(osmium::item_type type, osmium::object_id_type ref, std::string&& role) :
|
||||
m_type(type),
|
||||
m_ref(ref),
|
||||
m_role(std::move(role)) {
|
||||
}
|
||||
|
||||
osmium::item_type type() const noexcept {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
osmium::object_id_type ref() const noexcept {
|
||||
return m_ref;
|
||||
}
|
||||
|
||||
const char* role() const noexcept {
|
||||
return m_role.c_str();
|
||||
}
|
||||
|
||||
}; // class member_type_string
|
||||
|
||||
class comment_type {
|
||||
|
||||
osmium::Timestamp m_date;
|
||||
osmium::user_id_type m_uid;
|
||||
const char* m_user;
|
||||
const char* m_text;
|
||||
|
||||
public:
|
||||
|
||||
constexpr comment_type(osmium::Timestamp date, osmium::user_id_type uid, const char* user, const char* text) noexcept :
|
||||
m_date(date),
|
||||
m_uid(uid),
|
||||
m_user(user),
|
||||
m_text(text) {
|
||||
}
|
||||
|
||||
constexpr osmium::Timestamp date() const noexcept {
|
||||
return m_date;
|
||||
}
|
||||
|
||||
constexpr osmium::user_id_type uid() const noexcept {
|
||||
return m_uid;
|
||||
}
|
||||
|
||||
constexpr const char* user() const noexcept {
|
||||
return m_user;
|
||||
}
|
||||
|
||||
constexpr const char* text() const noexcept {
|
||||
return m_text;
|
||||
}
|
||||
|
||||
}; // class comment_type
|
||||
|
||||
namespace detail {
|
||||
|
||||
OSMIUM_ATTRIBUTE_ITER(tags_handler, tags_from_iterator_pair);
|
||||
|
||||
OSMIUM_ATTRIBUTE_ITER(nodes_handler, nodes_from_iterator_pair);
|
||||
|
||||
OSMIUM_ATTRIBUTE_ITER(members_handler, members_from_iterator_pair);
|
||||
|
||||
OSMIUM_ATTRIBUTE_ITER(discussion_handler, comments_from_iterator_pair);
|
||||
|
||||
OSMIUM_ATTRIBUTE_ITER(ring_handler, outer_ring_from_iterator_pair);
|
||||
OSMIUM_ATTRIBUTE_ITER(ring_handler, inner_ring_from_iterator_pair);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
OSMIUM_ATTRIBUTE(tags_handler, _tag, pair_of_cstrings)
|
||||
explicit _tag(const pair_of_cstrings& value) noexcept :
|
||||
type_wrapper(value) {}
|
||||
explicit _tag(const std::pair<const char* const, const char*>& value) :
|
||||
type_wrapper(pair_of_cstrings{value.first, value.second}) {}
|
||||
explicit _tag(const std::pair<const char*, const char* const>& value) :
|
||||
type_wrapper(pair_of_cstrings{value.first, value.second}) {}
|
||||
explicit _tag(const std::pair<const char*, const char*>& value) :
|
||||
type_wrapper(pair_of_cstrings{value.first, value.second}) {}
|
||||
explicit _tag(const pair_of_strings& value) :
|
||||
type_wrapper(std::make_pair(value.first.c_str(), value.second.c_str())) {}
|
||||
explicit _tag(const char* key, const char* val) :
|
||||
type_wrapper(std::make_pair(key, val)) {}
|
||||
explicit _tag(const std::string& key, const std::string& val) :
|
||||
type_wrapper(std::make_pair(key.c_str(), val.c_str())) {}
|
||||
};
|
||||
|
||||
template <typename TTagIterator>
|
||||
inline constexpr detail::tags_from_iterator_pair<TTagIterator> _tags(TTagIterator first, TTagIterator last) {
|
||||
return detail::tags_from_iterator_pair<TTagIterator>(first, last);
|
||||
}
|
||||
|
||||
template <typename TContainer>
|
||||
inline detail::tags_from_iterator_pair<typename TContainer::const_iterator> _tags(const TContainer& container) {
|
||||
return detail::tags_from_iterator_pair<typename TContainer::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
using tag_ilist = std::initializer_list<std::pair<const char*, const char*>>;
|
||||
inline detail::tags_from_iterator_pair<tag_ilist::const_iterator> _tags(const tag_ilist& container) {
|
||||
return detail::tags_from_iterator_pair<tag_ilist::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
|
||||
|
||||
OSMIUM_ATTRIBUTE(nodes_handler, _node, osmium::NodeRef)
|
||||
constexpr explicit _node(osmium::object_id_type value) noexcept :
|
||||
type_wrapper(NodeRef{value}) {}
|
||||
constexpr explicit _node(const NodeRef& value) noexcept :
|
||||
type_wrapper(value) {}
|
||||
};
|
||||
|
||||
template <typename TIdIterator>
|
||||
inline constexpr detail::nodes_from_iterator_pair<TIdIterator> _nodes(TIdIterator first, TIdIterator last) {
|
||||
return detail::nodes_from_iterator_pair<TIdIterator>(first, last);
|
||||
}
|
||||
|
||||
template <typename TContainer>
|
||||
inline detail::nodes_from_iterator_pair<typename TContainer::const_iterator> _nodes(const TContainer& container) {
|
||||
return detail::nodes_from_iterator_pair<typename TContainer::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
using object_id_ilist = std::initializer_list<osmium::object_id_type>;
|
||||
inline detail::nodes_from_iterator_pair<object_id_ilist::const_iterator> _nodes(const object_id_ilist& container) {
|
||||
return detail::nodes_from_iterator_pair<object_id_ilist::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
using node_ref_ilist = std::initializer_list<osmium::NodeRef>;
|
||||
inline detail::nodes_from_iterator_pair<node_ref_ilist::const_iterator> _nodes(const node_ref_ilist& container) {
|
||||
return detail::nodes_from_iterator_pair<node_ref_ilist::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
|
||||
OSMIUM_ATTRIBUTE(members_handler, _member, member_type)
|
||||
constexpr explicit _member(const member_type& value) noexcept :
|
||||
type_wrapper(value) {}
|
||||
constexpr explicit _member(osmium::item_type type, osmium::object_id_type id) noexcept :
|
||||
type_wrapper({type, id}) {}
|
||||
constexpr explicit _member(osmium::item_type type, osmium::object_id_type id, const char* role) noexcept :
|
||||
type_wrapper({type, id, role}) {}
|
||||
explicit _member(osmium::item_type type, osmium::object_id_type id, const std::string& role) noexcept :
|
||||
type_wrapper({type, id, role.c_str()}) {}
|
||||
explicit _member(const osmium::RelationMember& member) noexcept :
|
||||
type_wrapper({member.type(), member.ref(), member.role()}) {}
|
||||
};
|
||||
|
||||
template <typename TMemberIterator>
|
||||
inline constexpr detail::members_from_iterator_pair<TMemberIterator> _members(TMemberIterator first, TMemberIterator last) {
|
||||
return detail::members_from_iterator_pair<TMemberIterator>(first, last);
|
||||
}
|
||||
|
||||
template <typename TContainer>
|
||||
inline detail::members_from_iterator_pair<typename TContainer::const_iterator> _members(const TContainer& container) {
|
||||
return detail::members_from_iterator_pair<typename TContainer::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
using member_ilist = std::initializer_list<member_type>;
|
||||
inline detail::members_from_iterator_pair<member_ilist::const_iterator> _members(const member_ilist& container) {
|
||||
return detail::members_from_iterator_pair<member_ilist::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
|
||||
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(changeset_handler, _num_changes, osmium::num_changes_type);
|
||||
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(changeset_handler, _num_comments, osmium::num_comments_type);
|
||||
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(changeset_handler, _created_at, osmium::Timestamp);
|
||||
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(changeset_handler, _closed_at, osmium::Timestamp);
|
||||
|
||||
OSMIUM_ATTRIBUTE(discussion_handler, _comment, comment_type)
|
||||
constexpr explicit _comment(const comment_type& value) noexcept :
|
||||
type_wrapper(value) {}
|
||||
explicit _comment(const osmium::ChangesetComment& comment) noexcept :
|
||||
type_wrapper({comment.date(), comment.uid(), comment.user(), comment.text()}) {}
|
||||
};
|
||||
|
||||
template <typename TCommentIterator>
|
||||
inline constexpr detail::comments_from_iterator_pair<TCommentIterator> _comments(TCommentIterator first, TCommentIterator last) {
|
||||
return detail::comments_from_iterator_pair<TCommentIterator>(first, last);
|
||||
}
|
||||
|
||||
template <typename TContainer>
|
||||
inline detail::comments_from_iterator_pair<typename TContainer::const_iterator> _comments(const TContainer& container) {
|
||||
return detail::comments_from_iterator_pair<typename TContainer::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
using comment_ilist = std::initializer_list<comment_type>;
|
||||
inline detail::comments_from_iterator_pair<comment_ilist::const_iterator> _comments(const comment_ilist& container) {
|
||||
return detail::comments_from_iterator_pair<comment_ilist::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
|
||||
template <typename TIdIterator>
|
||||
inline constexpr detail::outer_ring_from_iterator_pair<TIdIterator> _outer_ring(TIdIterator first, TIdIterator last) {
|
||||
return detail::outer_ring_from_iterator_pair<TIdIterator>(first, last);
|
||||
}
|
||||
|
||||
template <typename TContainer>
|
||||
inline detail::outer_ring_from_iterator_pair<typename TContainer::const_iterator> _outer_ring(const TContainer& container) {
|
||||
return detail::outer_ring_from_iterator_pair<typename TContainer::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
using object_id_ilist = std::initializer_list<osmium::object_id_type>;
|
||||
inline detail::outer_ring_from_iterator_pair<object_id_ilist::const_iterator> _outer_ring(const object_id_ilist& container) {
|
||||
return detail::outer_ring_from_iterator_pair<object_id_ilist::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
using node_ref_ilist = std::initializer_list<osmium::NodeRef>;
|
||||
inline detail::outer_ring_from_iterator_pair<node_ref_ilist::const_iterator> _outer_ring(const node_ref_ilist& container) {
|
||||
return detail::outer_ring_from_iterator_pair<node_ref_ilist::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
template <typename TIdIterator>
|
||||
inline constexpr detail::inner_ring_from_iterator_pair<TIdIterator> _inner_ring(TIdIterator first, TIdIterator last) {
|
||||
return detail::inner_ring_from_iterator_pair<TIdIterator>(first, last);
|
||||
}
|
||||
|
||||
template <typename TContainer>
|
||||
inline detail::inner_ring_from_iterator_pair<typename TContainer::const_iterator> _inner_ring(const TContainer& container) {
|
||||
return detail::inner_ring_from_iterator_pair<typename TContainer::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
using object_id_ilist = std::initializer_list<osmium::object_id_type>;
|
||||
inline detail::inner_ring_from_iterator_pair<object_id_ilist::const_iterator> _inner_ring(const object_id_ilist& container) {
|
||||
return detail::inner_ring_from_iterator_pair<object_id_ilist::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
using node_ref_ilist = std::initializer_list<osmium::NodeRef>;
|
||||
inline detail::inner_ring_from_iterator_pair<node_ref_ilist::const_iterator> _inner_ring(const node_ref_ilist& container) {
|
||||
return detail::inner_ring_from_iterator_pair<node_ref_ilist::const_iterator>(std::begin(container), std::end(container));
|
||||
}
|
||||
|
||||
|
||||
} // namespace attr
|
||||
|
||||
#undef OSMIUM_ATTRIBUTE_ITER
|
||||
#undef OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR
|
||||
#undef OSMIUM_ATTRIBUTE
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct changeset_handler : public entity_handler {
|
||||
|
||||
template <typename TDummy>
|
||||
static void set_value(osmium::Changeset&, const TDummy&) noexcept {
|
||||
}
|
||||
|
||||
static void set_value(osmium::Changeset& changeset, attr::_cid id) noexcept {
|
||||
changeset.set_id(id.value);
|
||||
}
|
||||
|
||||
static void set_value(osmium::Changeset& changeset, attr::_num_changes num_changes) noexcept {
|
||||
changeset.set_num_changes(num_changes.value);
|
||||
}
|
||||
|
||||
static void set_value(osmium::Changeset& changeset, attr::_num_comments num_comments) noexcept {
|
||||
changeset.set_num_comments(num_comments.value);
|
||||
}
|
||||
|
||||
static void set_value(osmium::Changeset& changeset, attr::_created_at created_at) noexcept {
|
||||
changeset.set_created_at(created_at.value);
|
||||
}
|
||||
|
||||
static void set_value(osmium::Changeset& changeset, attr::_closed_at closed_at) noexcept {
|
||||
changeset.set_closed_at(closed_at.value);
|
||||
}
|
||||
|
||||
static void set_value(osmium::Changeset& changeset, attr::_uid uid) noexcept {
|
||||
changeset.set_uid(uid.value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct object_handler : public entity_handler {
|
||||
|
||||
template <typename TDummy>
|
||||
static void set_value(osmium::OSMObject&, const TDummy&) noexcept {
|
||||
}
|
||||
|
||||
static void set_value(osmium::OSMObject& object, attr::_id id) noexcept {
|
||||
object.set_id(id.value);
|
||||
}
|
||||
|
||||
static void set_value(osmium::OSMObject& object, attr::_version version) noexcept {
|
||||
object.set_version(version.value);
|
||||
}
|
||||
|
||||
static void set_value(osmium::OSMObject& object, attr::_visible visible) noexcept {
|
||||
object.set_visible(visible.value);
|
||||
}
|
||||
|
||||
static void set_value(osmium::OSMObject& object, attr::_deleted deleted) noexcept {
|
||||
object.set_deleted(deleted.value);
|
||||
}
|
||||
|
||||
static void set_value(osmium::OSMObject& object, attr::_timestamp timestamp) noexcept {
|
||||
object.set_timestamp(timestamp.value);
|
||||
}
|
||||
|
||||
static void set_value(osmium::OSMObject& object, attr::_cid changeset) noexcept {
|
||||
object.set_changeset(changeset.value);
|
||||
}
|
||||
|
||||
static void set_value(osmium::OSMObject& object, attr::_uid uid) noexcept {
|
||||
object.set_uid(uid.value);
|
||||
}
|
||||
|
||||
}; // object_handler
|
||||
|
||||
struct node_handler : public object_handler {
|
||||
|
||||
using object_handler::set_value;
|
||||
|
||||
static void set_value(osmium::Node& node, attr::_location location) noexcept {
|
||||
node.set_location(location.value);
|
||||
}
|
||||
|
||||
}; // node_handler
|
||||
|
||||
template <typename THandler, typename TBuilder, typename... TArgs>
|
||||
inline void add_basic(TBuilder& builder, const TArgs&... args) noexcept {
|
||||
(void)std::initializer_list<int>{
|
||||
(THandler::set_value(builder.object(), args), 0)...
|
||||
};
|
||||
}
|
||||
|
||||
// ==============================================================
|
||||
|
||||
template <typename... TArgs>
|
||||
inline constexpr const char* get_user(const attr::_user& user, const TArgs&...) noexcept {
|
||||
return user.value;
|
||||
}
|
||||
|
||||
inline constexpr const char* get_user() noexcept {
|
||||
return "";
|
||||
}
|
||||
|
||||
template <typename TFirst, typename... TRest>
|
||||
inline constexpr typename std::enable_if<!std::is_same<attr::_user, TFirst>::value, const char*>::type
|
||||
get_user(const TFirst&, const TRest&... args) noexcept {
|
||||
return get_user(args...);
|
||||
}
|
||||
|
||||
template <typename TBuilder, typename... TArgs>
|
||||
inline void add_user(TBuilder& builder, const TArgs&... args) {
|
||||
builder.set_user(get_user(args...));
|
||||
}
|
||||
|
||||
// ==============================================================
|
||||
|
||||
struct tags_handler {
|
||||
|
||||
template <typename TDummy>
|
||||
static void set_value(TagListBuilder&, const TDummy&) noexcept {
|
||||
}
|
||||
|
||||
static void set_value(TagListBuilder& builder, const attr::_tag& tag) {
|
||||
builder.add_tag(tag.value);
|
||||
}
|
||||
|
||||
template <typename TIterator>
|
||||
static void set_value(TagListBuilder& builder, const attr::detail::tags_from_iterator_pair<TIterator>& tags) {
|
||||
for (const auto& tag : tags) {
|
||||
builder.add_tag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
}; // struct tags_handler
|
||||
|
||||
struct nodes_handler {
|
||||
|
||||
template <typename TDummy>
|
||||
static void set_value(WayNodeListBuilder&, const TDummy&) noexcept {
|
||||
}
|
||||
|
||||
static void set_value(WayNodeListBuilder& builder, const attr::_node& node_ref) {
|
||||
builder.add_node_ref(node_ref.value);
|
||||
}
|
||||
|
||||
template <typename TIterator>
|
||||
static void set_value(WayNodeListBuilder& builder, const attr::detail::nodes_from_iterator_pair<TIterator>& nodes) {
|
||||
for (const auto& ref : nodes) {
|
||||
builder.add_node_ref(ref);
|
||||
}
|
||||
}
|
||||
|
||||
}; // struct nodes_handler
|
||||
|
||||
struct members_handler {
|
||||
|
||||
template <typename TDummy>
|
||||
static void set_value(RelationMemberListBuilder&, const TDummy&) noexcept {
|
||||
}
|
||||
|
||||
static void set_value(RelationMemberListBuilder& builder, const attr::_member& member) {
|
||||
builder.add_member(member.value.type(), member.value.ref(), member.value.role());
|
||||
}
|
||||
|
||||
template <typename TIterator>
|
||||
static void set_value(RelationMemberListBuilder& builder, const attr::detail::members_from_iterator_pair<TIterator>& members) {
|
||||
for (const auto& member : members) {
|
||||
builder.add_member(member.type(), member.ref(), member.role());
|
||||
}
|
||||
}
|
||||
|
||||
}; // struct members_handler
|
||||
|
||||
struct discussion_handler {
|
||||
|
||||
template <typename TDummy>
|
||||
static void set_value(ChangesetDiscussionBuilder&, const TDummy&) noexcept {
|
||||
}
|
||||
|
||||
static void set_value(ChangesetDiscussionBuilder& builder, const attr::_comment& comment) {
|
||||
builder.add_comment(comment.value.date(), comment.value.uid(), comment.value.user());
|
||||
builder.add_comment_text(comment.value.text());
|
||||
}
|
||||
|
||||
template <typename TIterator>
|
||||
static void set_value(ChangesetDiscussionBuilder& builder, const attr::detail::comments_from_iterator_pair<TIterator>& comments) {
|
||||
for (const auto& comment : comments) {
|
||||
builder.add_comment(comment.date(), comment.uid(), comment.user());
|
||||
builder.add_comment_text(comment.text());
|
||||
}
|
||||
}
|
||||
|
||||
}; // struct discussion_handler
|
||||
|
||||
struct ring_handler {
|
||||
|
||||
template <typename TDummy>
|
||||
static void set_value(AreaBuilder&, const TDummy&) noexcept {
|
||||
}
|
||||
|
||||
template <typename TIterator>
|
||||
static void set_value(AreaBuilder& parent, const attr::detail::outer_ring_from_iterator_pair<TIterator>& nodes) {
|
||||
OuterRingBuilder builder(parent.buffer(), &parent);
|
||||
for (const auto& ref : nodes) {
|
||||
builder.add_node_ref(ref);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TIterator>
|
||||
static void set_value(AreaBuilder& parent, const attr::detail::inner_ring_from_iterator_pair<TIterator>& nodes) {
|
||||
InnerRingBuilder builder(parent.buffer(), &parent);
|
||||
for (const auto& ref : nodes) {
|
||||
builder.add_node_ref(ref);
|
||||
}
|
||||
}
|
||||
|
||||
}; // struct ring_handler
|
||||
|
||||
// ==============================================================
|
||||
|
||||
template <typename TBuilder, typename THandler, typename... TArgs>
|
||||
inline typename std::enable_if<!is_handled_by<THandler, TArgs...>::value>::type
|
||||
add_list(osmium::builder::Builder&, const TArgs&...) noexcept {
|
||||
}
|
||||
|
||||
template <typename TBuilder, typename THandler, typename... TArgs>
|
||||
inline typename std::enable_if<is_handled_by<THandler, TArgs...>::value>::type
|
||||
add_list(osmium::builder::Builder& parent, const TArgs&... args) {
|
||||
TBuilder builder(parent.buffer(), &parent);
|
||||
(void)std::initializer_list<int>{
|
||||
(THandler::set_value(builder, args), 0)...
|
||||
};
|
||||
}
|
||||
|
||||
struct any_node_handlers : public node_handler, public tags_handler {};
|
||||
struct any_way_handlers : public object_handler, public tags_handler, public nodes_handler {};
|
||||
struct any_relation_handlers : public object_handler, public tags_handler, public members_handler {};
|
||||
struct any_area_handlers : public object_handler, public tags_handler, public ring_handler {};
|
||||
struct any_changeset_handlers : public changeset_handler, public tags_handler, public discussion_handler {};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
/**
|
||||
* Create a node using the given arguments and add it to the given buffer.
|
||||
*
|
||||
* @param buffer The buffer to which the node will be added.
|
||||
* @param args The attributes of the node.
|
||||
* @returns The position in the buffer where this node was added.
|
||||
*/
|
||||
template <typename... TArgs>
|
||||
inline size_t add_node(osmium::memory::Buffer& buffer, const TArgs&... args) {
|
||||
static_assert(sizeof...(args) > 0, "add_node() must have buffer and at least one additional argument");
|
||||
static_assert(detail::are_all_handled_by<detail::any_node_handlers, TArgs...>::value, "Attribute not allowed in add_node()");
|
||||
|
||||
{
|
||||
NodeBuilder builder(buffer);
|
||||
|
||||
detail::add_basic<detail::node_handler>(builder, args...);
|
||||
detail::add_user(builder, args...);
|
||||
detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
|
||||
}
|
||||
|
||||
return buffer.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a way using the given arguments and add it to the given buffer.
|
||||
*
|
||||
* @param buffer The buffer to which the way will be added.
|
||||
* @param args The attributes of the way.
|
||||
* @returns The position in the buffer where this way was added.
|
||||
*/
|
||||
template <typename... TArgs>
|
||||
inline size_t add_way(osmium::memory::Buffer& buffer, const TArgs&... args) {
|
||||
static_assert(sizeof...(args) > 0, "add_way() must have buffer and at least one additional argument");
|
||||
static_assert(detail::are_all_handled_by<detail::any_way_handlers, TArgs...>::value, "Attribute not allowed in add_way()");
|
||||
|
||||
{
|
||||
WayBuilder builder(buffer);
|
||||
|
||||
detail::add_basic<detail::object_handler>(builder, args...);
|
||||
detail::add_user(builder, args...);
|
||||
detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
|
||||
detail::add_list<WayNodeListBuilder, detail::nodes_handler>(builder, args...);
|
||||
}
|
||||
|
||||
return buffer.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a relation using the given arguments and add it to the given buffer.
|
||||
*
|
||||
* @param buffer The buffer to which the relation will be added.
|
||||
* @param args The attributes of the relation.
|
||||
* @returns The position in the buffer where this relation was added.
|
||||
*/
|
||||
template <typename... TArgs>
|
||||
inline size_t add_relation(osmium::memory::Buffer& buffer, const TArgs&... args) {
|
||||
static_assert(sizeof...(args) > 0, "add_relation() must have buffer and at least one additional argument");
|
||||
static_assert(detail::are_all_handled_by<detail::any_relation_handlers, TArgs...>::value, "Attribute not allowed in add_relation()");
|
||||
|
||||
{
|
||||
RelationBuilder builder(buffer);
|
||||
|
||||
detail::add_basic<detail::object_handler>(builder, args...);
|
||||
detail::add_user(builder, args...);
|
||||
detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
|
||||
detail::add_list<RelationMemberListBuilder, detail::members_handler>(builder, args...);
|
||||
}
|
||||
|
||||
return buffer.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a changeset using the given arguments and add it to the given buffer.
|
||||
*
|
||||
* @param buffer The buffer to which the changeset will be added.
|
||||
* @param args The attributes of the changeset.
|
||||
* @returns The position in the buffer where this changeset was added.
|
||||
*/
|
||||
template <typename... TArgs>
|
||||
inline size_t add_changeset(osmium::memory::Buffer& buffer, const TArgs&... args) {
|
||||
static_assert(sizeof...(args) > 0, "add_changeset() must have buffer and at least one additional argument");
|
||||
static_assert(detail::are_all_handled_by<detail::any_changeset_handlers, TArgs...>::value, "Attribute not allowed in add_changeset()");
|
||||
|
||||
{
|
||||
ChangesetBuilder builder(buffer);
|
||||
|
||||
detail::add_basic<detail::changeset_handler>(builder, args...);
|
||||
detail::add_user(builder, args...);
|
||||
detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
|
||||
detail::add_list<ChangesetDiscussionBuilder, detail::discussion_handler>(builder, args...);
|
||||
}
|
||||
|
||||
return buffer.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a area using the given arguments and add it to the given buffer.
|
||||
*
|
||||
* @param buffer The buffer to which the area will be added.
|
||||
* @param args The attributes of the area.
|
||||
* @returns The position in the buffer where this area was added.
|
||||
*/
|
||||
template <typename... TArgs>
|
||||
inline size_t add_area(osmium::memory::Buffer& buffer, const TArgs&... args) {
|
||||
static_assert(sizeof...(args) > 0, "add_area() must have buffer and at least one additional argument");
|
||||
static_assert(detail::are_all_handled_by<detail::any_area_handlers, TArgs...>::value, "Attribute not allowed in add_area()");
|
||||
|
||||
{
|
||||
AreaBuilder builder(buffer);
|
||||
|
||||
detail::add_basic<detail::object_handler>(builder, args...);
|
||||
detail::add_user(builder, args...);
|
||||
detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
|
||||
|
||||
(void)std::initializer_list<int>{
|
||||
(detail::ring_handler::set_value(builder, args), 0)...
|
||||
};
|
||||
}
|
||||
|
||||
return buffer.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WayNodeList using the given arguments and add it to the given buffer.
|
||||
*
|
||||
* @param buffer The buffer to which the list will be added.
|
||||
* @param args The contents of the list.
|
||||
* @returns The position in the buffer where this list was added.
|
||||
*/
|
||||
template <typename... TArgs>
|
||||
inline size_t add_way_node_list(osmium::memory::Buffer& buffer, const TArgs&... args) {
|
||||
static_assert(sizeof...(args) > 0, "add_way_node_list() must have buffer and at least one additional argument");
|
||||
static_assert(detail::are_all_handled_by<detail::nodes_handler, TArgs...>::value, "Attribute not allowed in add_way_node_list()");
|
||||
|
||||
{
|
||||
WayNodeListBuilder builder(buffer);
|
||||
(void)std::initializer_list<int>{
|
||||
(detail::nodes_handler::set_value(builder, args), 0)...
|
||||
};
|
||||
}
|
||||
|
||||
return buffer.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TagList using the given arguments and add it to the given buffer.
|
||||
*
|
||||
* @param buffer The buffer to which the list will be added.
|
||||
* @param args The contents of the list.
|
||||
* @returns The position in the buffer where this list was added.
|
||||
*/
|
||||
template <typename... TArgs>
|
||||
inline size_t add_tag_list(osmium::memory::Buffer& buffer, const TArgs&... args) {
|
||||
static_assert(sizeof...(args) > 0, "add_tag_list() must have buffer and at least one additional argument");
|
||||
static_assert(detail::are_all_handled_by<detail::tags_handler, TArgs...>::value, "Attribute not allowed in add_tag_list()");
|
||||
|
||||
{
|
||||
TagListBuilder builder(buffer);
|
||||
(void)std::initializer_list<int>{
|
||||
(detail::tags_handler::set_value(builder, args), 0)...
|
||||
};
|
||||
}
|
||||
|
||||
return buffer.commit();
|
||||
}
|
||||
|
||||
} // namespace builder
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_BUILDER_ATTR_HPP
|
||||
@@ -0,0 +1,235 @@
|
||||
#ifndef OSMIUM_BUILDER_BUILDER_HPP
|
||||
#define OSMIUM_BUILDER_BUILDER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/memory/item.hpp>
|
||||
#include <osmium/util/compatibility.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* @brief Classes for building OSM objects and other items in buffers
|
||||
*/
|
||||
namespace builder {
|
||||
|
||||
/**
|
||||
* Parent class for individual builder classes. Instantiate one of
|
||||
* its derived classes.
|
||||
*/
|
||||
class Builder {
|
||||
|
||||
osmium::memory::Buffer& m_buffer;
|
||||
Builder* m_parent;
|
||||
std::size_t m_item_offset;
|
||||
|
||||
Builder(const Builder&) = delete;
|
||||
Builder(Builder&&) = delete;
|
||||
|
||||
Builder& operator=(const Builder&) = delete;
|
||||
Builder& operator=(Builder&&) = delete;
|
||||
|
||||
protected:
|
||||
|
||||
explicit Builder(osmium::memory::Buffer& buffer, Builder* parent, osmium::memory::item_size_type size) :
|
||||
m_buffer(buffer),
|
||||
m_parent(parent),
|
||||
m_item_offset(buffer.written()) {
|
||||
reserve_space(size);
|
||||
assert(buffer.is_aligned());
|
||||
if (m_parent) {
|
||||
assert(m_buffer.builder_count() == 1 && "Only one sub-builder can be open at any time.");
|
||||
m_parent->add_size(size);
|
||||
} else {
|
||||
assert(m_buffer.builder_count() == 0 && "Only one builder can be open at any time.");
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
m_buffer.increment_builder_count();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
~Builder() = default;
|
||||
#else
|
||||
~Builder() noexcept {
|
||||
m_buffer.decrement_builder_count();
|
||||
}
|
||||
#endif
|
||||
|
||||
osmium::memory::Item& item() const {
|
||||
return *reinterpret_cast<osmium::memory::Item*>(m_buffer.data() + m_item_offset);
|
||||
}
|
||||
|
||||
unsigned char* reserve_space(std::size_t size) {
|
||||
return m_buffer.reserve_space(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add padding to buffer (if needed) to align data properly.
|
||||
*
|
||||
* This calculates how many padding bytes are needed and adds
|
||||
* as many zero bytes to the buffer. It also adds this number
|
||||
* to the size of the current item (if the "self" param is
|
||||
* true) and recursively to all the parent items.
|
||||
*
|
||||
* @param self If true add number of padding bytes to size
|
||||
* of current item. Size is always added to
|
||||
* parent item (if any).
|
||||
*
|
||||
*/
|
||||
void add_padding(bool self = false) {
|
||||
// We know the padding is only a very small number, so it will
|
||||
// always fit.
|
||||
const auto padding = static_cast<osmium::memory::item_size_type>(osmium::memory::align_bytes - (size() % osmium::memory::align_bytes));
|
||||
if (padding != osmium::memory::align_bytes) {
|
||||
std::fill_n(reserve_space(padding), padding, 0);
|
||||
if (self) {
|
||||
add_size(padding);
|
||||
} else if (m_parent) {
|
||||
m_parent->add_size(padding);
|
||||
assert(m_parent->size() % osmium::memory::align_bytes == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_size(osmium::memory::item_size_type size) {
|
||||
item().add_size(size);
|
||||
if (m_parent) {
|
||||
m_parent->add_size(size);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t size() const noexcept {
|
||||
return item().byte_size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserve space for an object of class T in buffer and return
|
||||
* pointer to it.
|
||||
*/
|
||||
template <typename T>
|
||||
T* reserve_space_for() {
|
||||
assert(m_buffer.is_aligned());
|
||||
return reinterpret_cast<T*>(reserve_space(sizeof(T)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Append data to buffer.
|
||||
*
|
||||
* @param data Pointer to data.
|
||||
* @param length Length of data in bytes. If data is a
|
||||
* \0-terminated string, length must contain the
|
||||
* \0 byte.
|
||||
* @returns The number of bytes appended (length).
|
||||
*/
|
||||
osmium::memory::item_size_type append(const char* data, const osmium::memory::item_size_type length) {
|
||||
unsigned char* target = reserve_space(length);
|
||||
std::copy_n(reinterpret_cast<const unsigned char*>(data), length, target);
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append data to buffer and append an additional \0.
|
||||
*
|
||||
* @param data Pointer to data.
|
||||
* @param length Length of data in bytes.
|
||||
* @returns The number of bytes appended (length + 1).
|
||||
*/
|
||||
osmium::memory::item_size_type append_with_zero(const char* data, const osmium::memory::item_size_type length) {
|
||||
unsigned char* target = reserve_space(length + 1);
|
||||
std::copy_n(reinterpret_cast<const unsigned char*>(data), length, target);
|
||||
target[length] = '\0';
|
||||
return length + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append \0-terminated string to buffer.
|
||||
*
|
||||
* @param str \0-terminated string.
|
||||
* @returns The number of bytes appended (strlen(str) + 1).
|
||||
*/
|
||||
osmium::memory::item_size_type append(const char* str) {
|
||||
return append(str, static_cast<osmium::memory::item_size_type>(std::strlen(str) + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Append '\0' to the buffer.
|
||||
*
|
||||
* @deprecated Use append_with_zero() instead.
|
||||
*
|
||||
* @returns The number of bytes appended (always 1).
|
||||
*/
|
||||
OSMIUM_DEPRECATED osmium::memory::item_size_type append_zero() {
|
||||
*reserve_space(1) = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Return the buffer this builder is using.
|
||||
osmium::memory::Buffer& buffer() noexcept {
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subitem to the object being built. This can be something
|
||||
* like a TagList or RelationMemberList.
|
||||
*/
|
||||
void add_item(const osmium::memory::Item& item) {
|
||||
m_buffer.add_item(item);
|
||||
add_size(item.padded_size());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the version of add_item() taking a
|
||||
* reference instead.
|
||||
*/
|
||||
OSMIUM_DEPRECATED void add_item(const osmium::memory::Item* item) {
|
||||
assert(item);
|
||||
add_item(*item);
|
||||
}
|
||||
|
||||
}; // class Builder
|
||||
|
||||
} // namespace builder
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_BUILDER_BUILDER_HPP
|
||||
@@ -0,0 +1,120 @@
|
||||
#ifndef OSMIUM_BUILDER_BUILDER_HELPER_HPP
|
||||
#define OSMIUM_BUILDER_BUILDER_HELPER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/builder/osm_object_builder.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/util/compatibility.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
class NodeRef;
|
||||
class TagList;
|
||||
class WayNodeList;
|
||||
|
||||
namespace builder {
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use osmium::builder::add_way_node_list() instead.
|
||||
*/
|
||||
OSMIUM_DEPRECATED inline const osmium::WayNodeList& build_way_node_list(osmium::memory::Buffer& buffer, const std::initializer_list<osmium::NodeRef>& nodes) {
|
||||
const size_t pos = buffer.committed();
|
||||
{
|
||||
osmium::builder::WayNodeListBuilder wnl_builder(buffer);
|
||||
for (const auto& node_ref : nodes) {
|
||||
wnl_builder.add_node_ref(node_ref);
|
||||
}
|
||||
}
|
||||
buffer.commit();
|
||||
return buffer.get<const osmium::WayNodeList>(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use osmium::builder::add_tag_list() instead.
|
||||
*/
|
||||
OSMIUM_DEPRECATED inline const osmium::TagList& build_tag_list(osmium::memory::Buffer& buffer, const std::initializer_list<std::pair<const char*, const char*>>& tags) {
|
||||
const size_t pos = buffer.committed();
|
||||
{
|
||||
osmium::builder::TagListBuilder tl_builder(buffer);
|
||||
for (const auto& p : tags) {
|
||||
tl_builder.add_tag(p.first, p.second);
|
||||
}
|
||||
}
|
||||
buffer.commit();
|
||||
return buffer.get<const osmium::TagList>(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use osmium::builder::add_tag_list() instead.
|
||||
*/
|
||||
OSMIUM_DEPRECATED inline const osmium::TagList& build_tag_list_from_map(osmium::memory::Buffer& buffer, const std::map<const char*, const char*>& tags) {
|
||||
const size_t pos = buffer.committed();
|
||||
{
|
||||
osmium::builder::TagListBuilder tl_builder(buffer);
|
||||
for (const auto& p : tags) {
|
||||
tl_builder.add_tag(p.first, p.second);
|
||||
}
|
||||
}
|
||||
buffer.commit();
|
||||
return buffer.get<const osmium::TagList>(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use osmium::builder::add_tag_list() instead.
|
||||
*/
|
||||
OSMIUM_DEPRECATED inline const osmium::TagList& build_tag_list_from_func(osmium::memory::Buffer& buffer, std::function<void(osmium::builder::TagListBuilder&)> func) {
|
||||
const size_t pos = buffer.committed();
|
||||
{
|
||||
osmium::builder::TagListBuilder tl_builder(buffer);
|
||||
func(tl_builder);
|
||||
}
|
||||
buffer.commit();
|
||||
return buffer.get<const osmium::TagList>(pos);
|
||||
}
|
||||
|
||||
} // namespace builder
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_BUILDER_BUILDER_HELPER_HPP
|
||||
@@ -0,0 +1,680 @@
|
||||
#ifndef OSMIUM_BUILDER_OSM_OBJECT_BUILDER_HPP
|
||||
#define OSMIUM_BUILDER_OSM_OBJECT_BUILDER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <limits>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/builder/builder.hpp>
|
||||
#include <osmium/memory/item.hpp>
|
||||
#include <osmium/osm/area.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/changeset.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/util/cast.hpp>
|
||||
#include <osmium/util/compatibility.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace memory {
|
||||
class Buffer;
|
||||
} // namespace memory
|
||||
|
||||
namespace builder {
|
||||
|
||||
class TagListBuilder : public Builder {
|
||||
|
||||
public:
|
||||
|
||||
explicit TagListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
|
||||
Builder(buffer, parent, sizeof(TagList)) {
|
||||
new (&item()) TagList{};
|
||||
}
|
||||
|
||||
explicit TagListBuilder(Builder& parent) :
|
||||
Builder(parent.buffer(), &parent, sizeof(TagList)) {
|
||||
new (&item()) TagList{};
|
||||
}
|
||||
|
||||
~TagListBuilder() {
|
||||
add_padding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tag to buffer.
|
||||
*
|
||||
* @param key Tag key (0-terminated string).
|
||||
* @param value Tag value (0-terminated string).
|
||||
*/
|
||||
void add_tag(const char* key, const char* value) {
|
||||
if (std::strlen(key) > osmium::max_osm_string_length) {
|
||||
throw std::length_error{"OSM tag key is too long"};
|
||||
}
|
||||
if (std::strlen(value) > osmium::max_osm_string_length) {
|
||||
throw std::length_error{"OSM tag value is too long"};
|
||||
}
|
||||
add_size(append(key));
|
||||
add_size(append(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tag to buffer.
|
||||
*
|
||||
* @param key Pointer to tag key.
|
||||
* @param key_length Length of key (not including the \0 byte).
|
||||
* @param value Pointer to tag value.
|
||||
* @param value_length Length of value (not including the \0 byte).
|
||||
*/
|
||||
void add_tag(const char* key, const std::size_t key_length, const char* value, const std::size_t value_length) {
|
||||
if (key_length > osmium::max_osm_string_length) {
|
||||
throw std::length_error{"OSM tag key is too long"};
|
||||
}
|
||||
if (value_length > osmium::max_osm_string_length) {
|
||||
throw std::length_error{"OSM tag value is too long"};
|
||||
}
|
||||
add_size(append_with_zero(key, osmium::memory::item_size_type(key_length)));
|
||||
add_size(append_with_zero(value, osmium::memory::item_size_type(value_length)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tag to buffer.
|
||||
*
|
||||
* @param key Tag key.
|
||||
* @param value Tag value.
|
||||
*/
|
||||
void add_tag(const std::string& key, const std::string& value) {
|
||||
if (key.size() > osmium::max_osm_string_length) {
|
||||
throw std::length_error{"OSM tag key is too long"};
|
||||
}
|
||||
if (value.size() > osmium::max_osm_string_length) {
|
||||
throw std::length_error{"OSM tag value is too long"};
|
||||
}
|
||||
add_size(append(key.data(), osmium::memory::item_size_type(key.size()) + 1));
|
||||
add_size(append(value.data(), osmium::memory::item_size_type(value.size()) + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tag to buffer.
|
||||
*
|
||||
* @param tag Tag.
|
||||
*/
|
||||
void add_tag(const osmium::Tag& tag) {
|
||||
add_size(append(tag.key()));
|
||||
add_size(append(tag.value()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tag to buffer.
|
||||
*
|
||||
* @param tag Pair of key/value 0-terminated strings.
|
||||
*/
|
||||
void add_tag(const std::pair<const char* const, const char* const>& tag) {
|
||||
add_tag(tag.first, tag.second);
|
||||
}
|
||||
void add_tag(const std::pair<const char* const, const char*>& tag) {
|
||||
add_tag(tag.first, tag.second);
|
||||
}
|
||||
void add_tag(const std::pair<const char*, const char* const>& tag) {
|
||||
add_tag(tag.first, tag.second);
|
||||
}
|
||||
void add_tag(const std::pair<const char*, const char*>& tag) {
|
||||
add_tag(tag.first, tag.second);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tag to buffer.
|
||||
*
|
||||
* @param tag Pair of std::string references.
|
||||
*/
|
||||
void add_tag(const std::pair<const std::string&, const std::string&>& tag) {
|
||||
add_tag(tag.first, tag.second);
|
||||
}
|
||||
|
||||
}; // class TagListBuilder
|
||||
|
||||
template <typename T>
|
||||
class NodeRefListBuilder : public Builder {
|
||||
|
||||
public:
|
||||
|
||||
explicit NodeRefListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
|
||||
Builder(buffer, parent, sizeof(T)) {
|
||||
new (&item()) T{};
|
||||
}
|
||||
|
||||
explicit NodeRefListBuilder(Builder& parent) :
|
||||
Builder(parent.buffer(), &parent, sizeof(T)) {
|
||||
new (&item()) T{};
|
||||
}
|
||||
|
||||
~NodeRefListBuilder() {
|
||||
add_padding();
|
||||
}
|
||||
|
||||
void add_node_ref(const NodeRef& node_ref) {
|
||||
new (reserve_space_for<osmium::NodeRef>()) osmium::NodeRef{node_ref};
|
||||
add_size(sizeof(osmium::NodeRef));
|
||||
}
|
||||
|
||||
void add_node_ref(const object_id_type ref, const osmium::Location& location = Location{}) {
|
||||
add_node_ref(NodeRef{ref, location});
|
||||
}
|
||||
|
||||
}; // class NodeRefListBuilder
|
||||
|
||||
using WayNodeListBuilder = NodeRefListBuilder<WayNodeList>;
|
||||
using OuterRingBuilder = NodeRefListBuilder<OuterRing>;
|
||||
using InnerRingBuilder = NodeRefListBuilder<InnerRing>;
|
||||
|
||||
class RelationMemberListBuilder : public Builder {
|
||||
|
||||
/**
|
||||
* Add role to buffer.
|
||||
*
|
||||
* @param member Relation member object where the length of the role
|
||||
* will be set.
|
||||
* @param role The role.
|
||||
* @param length Length of role (without \0 termination).
|
||||
* @throws std:length_error If role is longer than osmium::max_osm_string_length
|
||||
*/
|
||||
void add_role(osmium::RelationMember& member, const char* role, const std::size_t length) {
|
||||
if (length > osmium::max_osm_string_length) {
|
||||
throw std::length_error{"OSM relation member role is too long"};
|
||||
}
|
||||
member.set_role_size(osmium::string_size_type(length) + 1);
|
||||
add_size(append_with_zero(role, osmium::memory::item_size_type(length)));
|
||||
add_padding(true);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit RelationMemberListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
|
||||
Builder(buffer, parent, sizeof(RelationMemberList)) {
|
||||
new (&item()) RelationMemberList{};
|
||||
}
|
||||
|
||||
explicit RelationMemberListBuilder(Builder& parent) :
|
||||
Builder(parent.buffer(), &parent, sizeof(RelationMemberList)) {
|
||||
new (&item()) RelationMemberList{};
|
||||
}
|
||||
|
||||
~RelationMemberListBuilder() {
|
||||
add_padding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a member to the relation.
|
||||
*
|
||||
* @param type The type (node, way, or relation).
|
||||
* @param ref The ID of the member.
|
||||
* @param role The role of the member.
|
||||
* @param role_length Length of the role (without \0 termination).
|
||||
* @param full_member Optional pointer to the member object. If it
|
||||
* is available a copy will be added to the
|
||||
* relation.
|
||||
* @throws std:length_error If role_length is greater than
|
||||
* osmium::max_osm_string_length
|
||||
*/
|
||||
void add_member(osmium::item_type type, object_id_type ref, const char* role, const std::size_t role_length, const osmium::OSMObject* full_member = nullptr) {
|
||||
osmium::RelationMember* member = reserve_space_for<osmium::RelationMember>();
|
||||
new (member) osmium::RelationMember{ref, type, full_member != nullptr};
|
||||
add_size(sizeof(RelationMember));
|
||||
add_role(*member, role, role_length);
|
||||
if (full_member) {
|
||||
add_item(*full_member);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a member to the relation.
|
||||
*
|
||||
* @param type The type (node, way, or relation).
|
||||
* @param ref The ID of the member.
|
||||
* @param role The role of the member (\0 terminated string).
|
||||
* @param full_member Optional pointer to the member object. If it
|
||||
* is available a copy will be added to the
|
||||
* relation.
|
||||
* @throws std:length_error If role is longer than osmium::max_osm_string_length
|
||||
*/
|
||||
void add_member(osmium::item_type type, object_id_type ref, const char* role, const osmium::OSMObject* full_member = nullptr) {
|
||||
add_member(type, ref, role, std::strlen(role), full_member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a member to the relation.
|
||||
*
|
||||
* @param type The type (node, way, or relation).
|
||||
* @param ref The ID of the member.
|
||||
* @param role The role of the member.
|
||||
* @param full_member Optional pointer to the member object. If it
|
||||
* is available a copy will be added to the
|
||||
* relation.
|
||||
* @throws std:length_error If role is longer than osmium::max_osm_string_length
|
||||
*/
|
||||
void add_member(osmium::item_type type, object_id_type ref, const std::string& role, const osmium::OSMObject* full_member = nullptr) {
|
||||
add_member(type, ref, role.data(), role.size(), full_member);
|
||||
}
|
||||
|
||||
}; // class RelationMemberListBuilder
|
||||
|
||||
class ChangesetDiscussionBuilder : public Builder {
|
||||
|
||||
osmium::ChangesetComment* m_comment = nullptr;
|
||||
|
||||
void add_user(osmium::ChangesetComment& comment, const char* user, const std::size_t length) {
|
||||
if (length > osmium::max_osm_string_length) {
|
||||
throw std::length_error{"OSM user name is too long"};
|
||||
}
|
||||
comment.set_user_size(osmium::string_size_type(length) + 1);
|
||||
add_size(append_with_zero(user, osmium::memory::item_size_type(length)));
|
||||
}
|
||||
|
||||
void add_text(osmium::ChangesetComment& comment, const char* text, const std::size_t length) {
|
||||
if (length > std::numeric_limits<osmium::changeset_comment_size_type>::max() - 1) {
|
||||
throw std::length_error{"OSM changeset comment is too long"};
|
||||
}
|
||||
comment.set_text_size(osmium::changeset_comment_size_type(length) + 1);
|
||||
add_size(append_with_zero(text, osmium::memory::item_size_type(length)));
|
||||
add_padding(true);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit ChangesetDiscussionBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
|
||||
Builder(buffer, parent, sizeof(ChangesetDiscussion)) {
|
||||
new (&item()) ChangesetDiscussion{};
|
||||
}
|
||||
|
||||
explicit ChangesetDiscussionBuilder(Builder& parent) :
|
||||
Builder(parent.buffer(), &parent, sizeof(ChangesetDiscussion)) {
|
||||
new (&item()) ChangesetDiscussion{};
|
||||
}
|
||||
|
||||
~ChangesetDiscussionBuilder() {
|
||||
assert(!m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!");
|
||||
add_padding();
|
||||
}
|
||||
|
||||
void add_comment(osmium::Timestamp date, osmium::user_id_type uid, const char* user) {
|
||||
assert(!m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!");
|
||||
m_comment = reserve_space_for<osmium::ChangesetComment>();
|
||||
new (m_comment) osmium::ChangesetComment{date, uid};
|
||||
add_size(sizeof(ChangesetComment));
|
||||
add_user(*m_comment, user, std::strlen(user));
|
||||
}
|
||||
|
||||
void add_comment_text(const char* text) {
|
||||
assert(m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!");
|
||||
osmium::ChangesetComment& comment = *m_comment;
|
||||
m_comment = nullptr;
|
||||
add_text(comment, text, std::strlen(text));
|
||||
}
|
||||
|
||||
void add_comment_text(const std::string& text) {
|
||||
assert(m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!");
|
||||
osmium::ChangesetComment& comment = *m_comment;
|
||||
m_comment = nullptr;
|
||||
add_text(comment, text.c_str(), text.size());
|
||||
}
|
||||
|
||||
}; // class ChangesetDiscussionBuilder
|
||||
|
||||
#define OSMIUM_FORWARD(setter) \
|
||||
template <typename... TArgs> \
|
||||
type& setter(TArgs&&... args) { \
|
||||
object().setter(std::forward<TArgs>(args)...); \
|
||||
return static_cast<type&>(*this); \
|
||||
}
|
||||
|
||||
template <typename TDerived, typename T>
|
||||
class OSMObjectBuilder : public Builder {
|
||||
|
||||
using type = TDerived;
|
||||
|
||||
constexpr static const std::size_t min_size_for_user = osmium::memory::padded_length(sizeof(string_size_type) + 1);
|
||||
|
||||
public:
|
||||
|
||||
explicit OSMObjectBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
|
||||
Builder(buffer, parent, sizeof(T) + min_size_for_user) {
|
||||
new (&item()) T{};
|
||||
add_size(min_size_for_user);
|
||||
std::fill_n(object().data() + sizeof(T), min_size_for_user, 0);
|
||||
object().set_user_size(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to the object buing built.
|
||||
*
|
||||
* Note that this reference will be invalidated by every action
|
||||
* on the builder that might make the buffer grow. This includes
|
||||
* calls to set_user() and any time a new sub-builder is created.
|
||||
*/
|
||||
T& object() noexcept {
|
||||
return static_cast<T&>(item());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a const reference to the object buing built.
|
||||
*
|
||||
* Note that this reference will be invalidated by every action
|
||||
* on the builder that might make the buffer grow. This includes
|
||||
* calls to set_user() and any time a new sub-builder is created.
|
||||
*/
|
||||
const T& cobject() const noexcept {
|
||||
return static_cast<const T&>(item());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user name.
|
||||
*
|
||||
* @param user Pointer to user name.
|
||||
* @param length Length of user name (without \0 termination).
|
||||
*/
|
||||
TDerived& set_user(const char* user, const string_size_type length) {
|
||||
const auto size_of_object = sizeof(T) + sizeof(string_size_type);
|
||||
assert(cobject().user_size() == 1 && (size() <= size_of_object + osmium::memory::padded_length(1))
|
||||
&& "set_user() must be called at most once and before any sub-builders");
|
||||
const auto available_space = min_size_for_user - sizeof(string_size_type) - 1;
|
||||
if (length > available_space) {
|
||||
const auto space_needed = osmium::memory::padded_length(length - available_space);
|
||||
std::fill_n(reserve_space(space_needed), space_needed, 0);
|
||||
add_size(static_cast<uint32_t>(space_needed));
|
||||
}
|
||||
std::copy_n(user, length, object().data() + size_of_object);
|
||||
object().set_user_size(length + 1);
|
||||
|
||||
return static_cast<TDerived&>(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user name.
|
||||
*
|
||||
* @param user Pointer to \0-terminated user name.
|
||||
*/
|
||||
TDerived& set_user(const char* user) {
|
||||
return set_user(user, static_cast_with_assert<string_size_type>(std::strlen(user)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user name.
|
||||
*
|
||||
* @param user User name.
|
||||
*/
|
||||
TDerived& set_user(const std::string& user) {
|
||||
return set_user(user.data(), static_cast_with_assert<string_size_type>(user.size()));
|
||||
}
|
||||
|
||||
/// @deprecated Use set_user(...) instead.
|
||||
template <typename... TArgs>
|
||||
OSMIUM_DEPRECATED void add_user(TArgs&&... args) {
|
||||
set_user(std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
OSMIUM_FORWARD(set_id)
|
||||
OSMIUM_FORWARD(set_visible)
|
||||
OSMIUM_FORWARD(set_deleted)
|
||||
OSMIUM_FORWARD(set_version)
|
||||
OSMIUM_FORWARD(set_changeset)
|
||||
OSMIUM_FORWARD(set_uid)
|
||||
OSMIUM_FORWARD(set_uid_from_signed)
|
||||
OSMIUM_FORWARD(set_timestamp)
|
||||
OSMIUM_FORWARD(set_attribute)
|
||||
OSMIUM_FORWARD(set_removed)
|
||||
|
||||
void add_tags(const std::initializer_list<std::pair<const char*, const char*>>& tags) {
|
||||
osmium::builder::TagListBuilder tl_builder{buffer(), this};
|
||||
for (const auto& p : tags) {
|
||||
tl_builder.add_tag(p.first, p.second);
|
||||
}
|
||||
}
|
||||
|
||||
}; // class OSMObjectBuilder
|
||||
|
||||
class NodeBuilder : public OSMObjectBuilder<NodeBuilder, Node> {
|
||||
|
||||
using type = NodeBuilder;
|
||||
|
||||
public:
|
||||
|
||||
explicit NodeBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
|
||||
OSMObjectBuilder<NodeBuilder, Node>(buffer, parent) {
|
||||
}
|
||||
|
||||
explicit NodeBuilder(Builder& parent) :
|
||||
OSMObjectBuilder<NodeBuilder, Node>(parent.buffer(), &parent) {
|
||||
}
|
||||
|
||||
OSMIUM_FORWARD(set_location)
|
||||
|
||||
}; // class NodeBuilder
|
||||
|
||||
class WayBuilder : public OSMObjectBuilder<WayBuilder, Way> {
|
||||
|
||||
using type = WayBuilder;
|
||||
|
||||
public:
|
||||
|
||||
explicit WayBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
|
||||
OSMObjectBuilder<WayBuilder, Way>(buffer, parent) {
|
||||
}
|
||||
|
||||
explicit WayBuilder(Builder& parent) :
|
||||
OSMObjectBuilder<WayBuilder, Way>(parent.buffer(), &parent) {
|
||||
}
|
||||
|
||||
void add_node_refs(const std::initializer_list<osmium::NodeRef>& nodes) {
|
||||
osmium::builder::WayNodeListBuilder builder{buffer(), this};
|
||||
for (const auto& node_ref : nodes) {
|
||||
builder.add_node_ref(node_ref);
|
||||
}
|
||||
}
|
||||
|
||||
}; // class WayBuilder
|
||||
|
||||
class RelationBuilder : public OSMObjectBuilder<RelationBuilder, Relation> {
|
||||
|
||||
using type = RelationBuilder;
|
||||
|
||||
public:
|
||||
|
||||
explicit RelationBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
|
||||
OSMObjectBuilder<RelationBuilder, Relation>(buffer, parent) {
|
||||
}
|
||||
|
||||
explicit RelationBuilder(Builder& parent) :
|
||||
OSMObjectBuilder<RelationBuilder, Relation>(parent.buffer(), &parent) {
|
||||
}
|
||||
|
||||
}; // class RelationBuilder
|
||||
|
||||
class AreaBuilder : public OSMObjectBuilder<AreaBuilder, Area> {
|
||||
|
||||
using type = AreaBuilder;
|
||||
|
||||
public:
|
||||
|
||||
explicit AreaBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
|
||||
OSMObjectBuilder<AreaBuilder, Area>(buffer, parent) {
|
||||
}
|
||||
|
||||
explicit AreaBuilder(Builder& parent) :
|
||||
OSMObjectBuilder<AreaBuilder, Area>(parent.buffer(), &parent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize area attributes from the attributes of the given object.
|
||||
*/
|
||||
void initialize_from_object(const osmium::OSMObject& source) {
|
||||
set_id(osmium::object_id_to_area_id(source.id(), source.type()));
|
||||
set_version(source.version());
|
||||
set_changeset(source.changeset());
|
||||
set_timestamp(source.timestamp());
|
||||
set_visible(source.visible());
|
||||
set_uid(source.uid());
|
||||
set_user(source.user());
|
||||
}
|
||||
|
||||
}; // class AreaBuilder
|
||||
|
||||
class ChangesetBuilder : public Builder {
|
||||
|
||||
using type = ChangesetBuilder;
|
||||
|
||||
constexpr static const std::size_t min_size_for_user = osmium::memory::padded_length(1);
|
||||
|
||||
public:
|
||||
|
||||
explicit ChangesetBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
|
||||
Builder(buffer, parent, sizeof(Changeset) + min_size_for_user) {
|
||||
new (&item()) Changeset{};
|
||||
add_size(min_size_for_user);
|
||||
std::fill_n(object().data() + sizeof(Changeset), min_size_for_user, 0);
|
||||
object().set_user_size(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to the changeset buing built.
|
||||
*
|
||||
* Note that this reference will be invalidated by every action
|
||||
* on the builder that might make the buffer grow. This includes
|
||||
* calls to set_user() and any time a new sub-builder is created.
|
||||
*/
|
||||
Changeset& object() noexcept {
|
||||
return static_cast<Changeset&>(item());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a const reference to the changeset buing built.
|
||||
*
|
||||
* Note that this reference will be invalidated by every action
|
||||
* on the builder that might make the buffer grow. This includes
|
||||
* calls to set_user() and any time a new sub-builder is created.
|
||||
*/
|
||||
const Changeset& cobject() const noexcept {
|
||||
return static_cast<const Changeset&>(item());
|
||||
}
|
||||
|
||||
OSMIUM_FORWARD(set_id)
|
||||
OSMIUM_FORWARD(set_uid)
|
||||
OSMIUM_FORWARD(set_uid_from_signed)
|
||||
OSMIUM_FORWARD(set_created_at)
|
||||
OSMIUM_FORWARD(set_closed_at)
|
||||
OSMIUM_FORWARD(set_num_changes)
|
||||
OSMIUM_FORWARD(set_num_comments)
|
||||
OSMIUM_FORWARD(set_attribute)
|
||||
OSMIUM_FORWARD(set_removed)
|
||||
|
||||
// @deprecated Use set_bounds() instead.
|
||||
OSMIUM_DEPRECATED osmium::Box& bounds() noexcept {
|
||||
return object().bounds();
|
||||
}
|
||||
|
||||
ChangesetBuilder& set_bounds(const osmium::Box& box) noexcept {
|
||||
object().bounds() = box;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user name.
|
||||
*
|
||||
* @param user Pointer to user name.
|
||||
* @param length Length of user name (without \0 termination).
|
||||
*/
|
||||
ChangesetBuilder& set_user(const char* user, const string_size_type length) {
|
||||
assert(cobject().user_size() == 1 && (size() <= sizeof(Changeset) + osmium::memory::padded_length(1))
|
||||
&& "set_user() must be called at most once and before any sub-builders");
|
||||
const auto available_space = min_size_for_user - 1;
|
||||
if (length > available_space) {
|
||||
const auto space_needed = osmium::memory::padded_length(length - available_space);
|
||||
std::fill_n(reserve_space(space_needed), space_needed, 0);
|
||||
add_size(static_cast<uint32_t>(space_needed));
|
||||
}
|
||||
std::copy_n(user, length, object().data() + sizeof(Changeset));
|
||||
object().set_user_size(length + 1);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user name.
|
||||
*
|
||||
* @param user Pointer to \0-terminated user name.
|
||||
*/
|
||||
ChangesetBuilder& set_user(const char* user) {
|
||||
return set_user(user, static_cast_with_assert<string_size_type>(std::strlen(user)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user name.
|
||||
*
|
||||
* @param user User name.
|
||||
*/
|
||||
ChangesetBuilder& set_user(const std::string& user) {
|
||||
return set_user(user.data(), static_cast_with_assert<string_size_type>(user.size()));
|
||||
}
|
||||
|
||||
/// @deprecated Use set_user(...) instead.
|
||||
template <typename... TArgs>
|
||||
OSMIUM_DEPRECATED void add_user(TArgs&&... args) {
|
||||
set_user(std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
}; // class ChangesetBuilder
|
||||
|
||||
#undef OSMIUM_FORWARD
|
||||
|
||||
} // namespace builder
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_BUILDER_OSM_OBJECT_BUILDER_HPP
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef OSMIUM_DIFF_HANDLER_HPP
|
||||
#define OSMIUM_DIFF_HANDLER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/osm/diff_object.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* @brief Osmium diff handlers provide access to differences between OSM object versions
|
||||
*/
|
||||
namespace diff_handler {
|
||||
|
||||
class DiffHandler {
|
||||
|
||||
public:
|
||||
|
||||
DiffHandler() = default;
|
||||
|
||||
void node(const osmium::DiffNode&) const noexcept {
|
||||
}
|
||||
|
||||
void way(const osmium::DiffWay&) const noexcept {
|
||||
}
|
||||
|
||||
void relation(const osmium::DiffRelation&) const noexcept {
|
||||
}
|
||||
|
||||
}; // class DiffHandler
|
||||
|
||||
} // namespace diff_handler
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_DIFF_HANDLER_HPP
|
||||
@@ -0,0 +1,143 @@
|
||||
#ifndef OSMIUM_DIFF_ITERATOR_HPP
|
||||
#define OSMIUM_DIFF_ITERATOR_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/osm/diff_object.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
class OSMObject;
|
||||
|
||||
/**
|
||||
* An input iterator wrapping any iterator over OSMObjects. When
|
||||
* dereferenced it will yield DiffObject objects pointing to the
|
||||
* underlying OSMObjects.
|
||||
*/
|
||||
template <typename TBasicIterator>
|
||||
class DiffIterator {
|
||||
|
||||
static_assert(std::is_base_of<osmium::OSMObject, typename TBasicIterator::value_type>::value, "TBasicIterator::value_type must derive from osmium::OSMObject");
|
||||
|
||||
TBasicIterator m_prev;
|
||||
TBasicIterator m_curr;
|
||||
TBasicIterator m_next;
|
||||
|
||||
const TBasicIterator m_end;
|
||||
|
||||
mutable osmium::DiffObject m_diff;
|
||||
|
||||
void set_diff() const noexcept {
|
||||
assert(m_curr != m_end);
|
||||
|
||||
const bool use_curr_for_prev = m_prev->type() != m_curr->type() || m_prev->id() != m_curr->id();
|
||||
const bool use_curr_for_next = m_next == m_end || m_next->type() != m_curr->type() || m_next->id() != m_curr->id();
|
||||
|
||||
m_diff = std::move(osmium::DiffObject{
|
||||
*(use_curr_for_prev ? m_curr : m_prev),
|
||||
*m_curr,
|
||||
*(use_curr_for_next ? m_curr : m_next)
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using value_type = const osmium::DiffObject;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
DiffIterator(TBasicIterator begin, TBasicIterator end) :
|
||||
m_prev(begin),
|
||||
m_curr(begin),
|
||||
m_next(begin == end ? begin : ++begin),
|
||||
m_end(std::move(end)),
|
||||
m_diff() {
|
||||
}
|
||||
|
||||
DiffIterator& operator++() {
|
||||
m_prev = std::move(m_curr);
|
||||
m_curr = m_next;
|
||||
|
||||
if (m_next != m_end) {
|
||||
++m_next;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DiffIterator operator++(int) {
|
||||
DiffIterator tmp{*this};
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const DiffIterator& rhs) const noexcept {
|
||||
return m_curr == rhs.m_curr && m_end == rhs.m_end;
|
||||
}
|
||||
|
||||
bool operator!=(const DiffIterator& rhs) const noexcept {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
reference operator*() const noexcept {
|
||||
set_diff();
|
||||
return m_diff;
|
||||
}
|
||||
|
||||
pointer operator->() const noexcept {
|
||||
set_diff();
|
||||
return &m_diff;
|
||||
}
|
||||
|
||||
}; // class DiffIterator
|
||||
|
||||
/**
|
||||
* Create a DiffIterator based on the given iterators.
|
||||
*/
|
||||
template <typename TBasicIterator>
|
||||
inline DiffIterator<TBasicIterator> make_diff_iterator(TBasicIterator begin,
|
||||
TBasicIterator end) {
|
||||
return DiffIterator<TBasicIterator>{begin, end};
|
||||
}
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_DIFF_ITERATOR_HPP
|
||||
@@ -0,0 +1,104 @@
|
||||
#ifndef OSMIUM_DIFF_VISITOR_HPP
|
||||
#define OSMIUM_DIFF_VISITOR_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/diff_iterator.hpp>
|
||||
#include <osmium/io/input_iterator.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm/diff_object.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename THandler>
|
||||
inline void apply_diff_iterator_recurse(const osmium::DiffObject& diff, THandler& handler) {
|
||||
switch (diff.type()) {
|
||||
case osmium::item_type::node:
|
||||
handler.node(static_cast<const osmium::DiffNode&>(diff));
|
||||
break;
|
||||
case osmium::item_type::way:
|
||||
handler.way(static_cast<const osmium::DiffWay&>(diff));
|
||||
break;
|
||||
case osmium::item_type::relation:
|
||||
handler.relation(static_cast<const osmium::DiffRelation&>(diff));
|
||||
break;
|
||||
default:
|
||||
throw osmium::unknown_type{};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename THandler, typename... TRest>
|
||||
inline void apply_diff_iterator_recurse(const osmium::DiffObject& diff, THandler& handler, TRest&... more) {
|
||||
apply_diff_iterator_recurse(diff, handler);
|
||||
apply_diff_iterator_recurse(diff, more...);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename TIterator, typename... THandlers>
|
||||
inline void apply_diff(TIterator it, TIterator end, THandlers&... handlers) {
|
||||
using diff_iterator = osmium::DiffIterator<TIterator>;
|
||||
|
||||
diff_iterator dit{it, end};
|
||||
diff_iterator dend{end, end};
|
||||
|
||||
for (; dit != dend; ++dit) {
|
||||
detail::apply_diff_iterator_recurse(*dit, handlers...);
|
||||
}
|
||||
}
|
||||
|
||||
class OSMObject;
|
||||
|
||||
template <typename TSource, typename... THandlers>
|
||||
inline void apply_diff(TSource& source, THandlers&... handlers) {
|
||||
apply_diff(osmium::io::InputIterator<TSource, osmium::OSMObject>{source},
|
||||
osmium::io::InputIterator<TSource, osmium::OSMObject>{},
|
||||
handlers...);
|
||||
}
|
||||
|
||||
template <typename... THandlers>
|
||||
inline void apply_diff(osmium::memory::Buffer& buffer, THandlers&... handlers) {
|
||||
apply_diff(buffer.begin(), buffer.end(), handlers...);
|
||||
}
|
||||
|
||||
template <typename... THandlers>
|
||||
inline void apply_diff(const osmium::memory::Buffer& buffer, THandlers&... handlers) {
|
||||
apply_diff(buffer.cbegin(), buffer.cend(), handlers...);
|
||||
}
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_DIFF_VISITOR_HPP
|
||||
@@ -0,0 +1,195 @@
|
||||
#ifndef OSMIUM_DYNAMIC_HANDLER_HPP
|
||||
#define OSMIUM_DYNAMIC_HANDLER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/handler.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
class Node;
|
||||
class Way;
|
||||
class Relation;
|
||||
class Area;
|
||||
class Changeset;
|
||||
|
||||
namespace handler {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class HandlerWrapperBase {
|
||||
|
||||
public:
|
||||
|
||||
virtual ~HandlerWrapperBase() = default;
|
||||
|
||||
virtual void node(const osmium::Node&) {
|
||||
}
|
||||
|
||||
virtual void way(const osmium::Way&) {
|
||||
}
|
||||
|
||||
virtual void relation(const osmium::Relation&) {
|
||||
}
|
||||
|
||||
virtual void area(const osmium::Area&) {
|
||||
}
|
||||
|
||||
virtual void changeset(const osmium::Changeset&) {
|
||||
}
|
||||
|
||||
virtual void flush() {
|
||||
}
|
||||
|
||||
}; // class HandlerWrapperBase
|
||||
|
||||
|
||||
// The following uses trick from
|
||||
// http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence
|
||||
// to either call handler style functions or visitor style operator().
|
||||
|
||||
#define OSMIUM_DYNAMIC_HANDLER_DISPATCH(_name_, _type_) \
|
||||
template <typename THandler> \
|
||||
auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, int) -> decltype(handler._name_(object), void()) { \
|
||||
handler._name_(object); \
|
||||
} \
|
||||
template <typename THandler> \
|
||||
auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) -> decltype(handler(object), void()) { \
|
||||
handler(object); \
|
||||
}
|
||||
|
||||
OSMIUM_DYNAMIC_HANDLER_DISPATCH(node, Node)
|
||||
OSMIUM_DYNAMIC_HANDLER_DISPATCH(way, Way)
|
||||
OSMIUM_DYNAMIC_HANDLER_DISPATCH(relation, Relation)
|
||||
OSMIUM_DYNAMIC_HANDLER_DISPATCH(changeset, Changeset)
|
||||
OSMIUM_DYNAMIC_HANDLER_DISPATCH(area, Area)
|
||||
|
||||
template <typename THandler>
|
||||
auto flush_dispatch(THandler& handler, int) -> decltype(handler.flush(), void()) {
|
||||
handler.flush();
|
||||
}
|
||||
|
||||
template <typename THandler>
|
||||
void flush_dispatch(THandler&, long) {
|
||||
}
|
||||
|
||||
template <typename THandler>
|
||||
class HandlerWrapper : public HandlerWrapperBase {
|
||||
|
||||
THandler m_handler;
|
||||
|
||||
public:
|
||||
|
||||
template <typename... TArgs>
|
||||
explicit HandlerWrapper(TArgs&&... args) :
|
||||
m_handler(std::forward<TArgs>(args)...) {
|
||||
}
|
||||
|
||||
void node(const osmium::Node& node) final {
|
||||
node_dispatch(m_handler, node, 0);
|
||||
}
|
||||
|
||||
void way(const osmium::Way& way) final {
|
||||
way_dispatch(m_handler, way, 0);
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) final {
|
||||
relation_dispatch(m_handler, relation, 0);
|
||||
}
|
||||
|
||||
void area(const osmium::Area& area) final {
|
||||
area_dispatch(m_handler, area, 0);
|
||||
}
|
||||
|
||||
void changeset(const osmium::Changeset& changeset) final {
|
||||
changeset_dispatch(m_handler, changeset, 0);
|
||||
}
|
||||
|
||||
void flush() final {
|
||||
flush_dispatch(m_handler, 0);
|
||||
}
|
||||
|
||||
}; // class HandlerWrapper
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class DynamicHandler : public osmium::handler::Handler {
|
||||
|
||||
using impl_ptr = std::unique_ptr<osmium::handler::detail::HandlerWrapperBase>;
|
||||
impl_ptr m_impl;
|
||||
|
||||
public:
|
||||
|
||||
DynamicHandler() :
|
||||
m_impl(new osmium::handler::detail::HandlerWrapperBase) {
|
||||
}
|
||||
|
||||
template <typename THandler, typename... TArgs>
|
||||
void set(TArgs&&... args) {
|
||||
m_impl.reset(new osmium::handler::detail::HandlerWrapper<THandler>{std::forward<TArgs>(args)...});
|
||||
}
|
||||
|
||||
void node(const osmium::Node& node) {
|
||||
m_impl->node(node);
|
||||
}
|
||||
|
||||
void way(const osmium::Way& way) {
|
||||
m_impl->way(way);
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) {
|
||||
m_impl->relation(relation);
|
||||
}
|
||||
|
||||
void area(const osmium::Area& area) {
|
||||
m_impl->area(area);
|
||||
}
|
||||
|
||||
void changeset(const osmium::Changeset& changeset) {
|
||||
m_impl->changeset(changeset);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
m_impl->flush();
|
||||
}
|
||||
|
||||
}; // class DynamicHandler
|
||||
|
||||
} // namespace handler
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_DYNAMIC_HANDLER_HPP
|
||||
@@ -0,0 +1,138 @@
|
||||
#ifndef OSMIUM_EXPERIMENTAL_FLEX_READER_HPP
|
||||
#define OSMIUM_EXPERIMENTAL_FLEX_READER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/area/assembler.hpp>
|
||||
#include <osmium/area/multipolygon_collector.hpp>
|
||||
#include <osmium/handler/node_locations_for_ways.hpp> // IWYU pragma: keep
|
||||
#include <osmium/io/file.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/io/reader.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm/entity_bits.hpp>
|
||||
#include <osmium/visitor.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* @brief Experimental code that is not "officially" supported.
|
||||
*/
|
||||
namespace experimental {
|
||||
|
||||
template <typename TLocationHandler>
|
||||
class FlexReader {
|
||||
|
||||
bool m_with_areas;
|
||||
osmium::osm_entity_bits::type m_entities;
|
||||
|
||||
TLocationHandler& m_location_handler;
|
||||
|
||||
osmium::io::Reader m_reader;
|
||||
osmium::area::Assembler::config_type m_assembler_config;
|
||||
osmium::area::MultipolygonCollector<osmium::area::Assembler> m_collector;
|
||||
|
||||
public:
|
||||
|
||||
explicit FlexReader(const osmium::io::File& file, TLocationHandler& location_handler, osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::nwr) :
|
||||
m_with_areas((entities & osmium::osm_entity_bits::area) != 0),
|
||||
m_entities((entities & ~osmium::osm_entity_bits::area) | (m_with_areas ? osmium::osm_entity_bits::node | osmium::osm_entity_bits::way : osmium::osm_entity_bits::nothing)),
|
||||
m_location_handler(location_handler),
|
||||
m_reader(file, m_entities),
|
||||
m_assembler_config(),
|
||||
m_collector(m_assembler_config)
|
||||
{
|
||||
m_location_handler.ignore_errors();
|
||||
if (m_with_areas) {
|
||||
osmium::io::Reader reader{file, osmium::osm_entity_bits::relation};
|
||||
m_collector.read_relations(reader);
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
explicit FlexReader(const std::string& filename, TLocationHandler& location_handler, osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::nwr) :
|
||||
FlexReader(osmium::io::File(filename), location_handler, entities) {
|
||||
}
|
||||
|
||||
explicit FlexReader(const char* filename, TLocationHandler& location_handler, osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::nwr) :
|
||||
FlexReader(osmium::io::File(filename), location_handler, entities) {
|
||||
}
|
||||
|
||||
osmium::memory::Buffer read() {
|
||||
osmium::memory::Buffer buffer = m_reader.read();
|
||||
|
||||
if (buffer) {
|
||||
if (m_with_areas) {
|
||||
std::vector<osmium::memory::Buffer> area_buffers;
|
||||
osmium::apply(buffer, m_location_handler, m_collector.handler([&area_buffers](osmium::memory::Buffer&& area_buffer) {
|
||||
area_buffers.push_back(std::move(area_buffer));
|
||||
}));
|
||||
for (const osmium::memory::Buffer& b : area_buffers) {
|
||||
buffer.add_buffer(b);
|
||||
buffer.commit();
|
||||
}
|
||||
} else if (m_entities & (osmium::osm_entity_bits::node | osmium::osm_entity_bits::way)) {
|
||||
osmium::apply(buffer, m_location_handler);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
osmium::io::Header header() {
|
||||
return m_reader.header();
|
||||
}
|
||||
|
||||
void close() {
|
||||
return m_reader.close();
|
||||
}
|
||||
|
||||
bool eof() const {
|
||||
return m_reader.eof();
|
||||
}
|
||||
|
||||
const osmium::area::MultipolygonCollector<osmium::area::Assembler>& collector() const {
|
||||
return m_collector;
|
||||
}
|
||||
|
||||
}; // class FlexReader
|
||||
|
||||
} // namespace experimental
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_EXPERIMENTAL_FLEX_READER_HPP
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
#ifndef OSMIUM_FWD_HPP
|
||||
#define OSMIUM_FWD_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* This file contains forward declarations for commonly used Osmium classes.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace osmium {
|
||||
|
||||
class Area;
|
||||
class Box;
|
||||
class Changeset;
|
||||
class ChangesetComment;
|
||||
class ChangesetDiscussion;
|
||||
class InnerRing;
|
||||
class Location;
|
||||
class Node;
|
||||
class NodeRef;
|
||||
class NodeRefList;
|
||||
class OSMEntity;
|
||||
class OSMObject;
|
||||
class OuterRing;
|
||||
class Relation;
|
||||
class RelationMemberList;
|
||||
class Segment;
|
||||
class Tag;
|
||||
class TagList;
|
||||
class Timestamp;
|
||||
class Way;
|
||||
class WayNodeList;
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_FWD_HPP
|
||||
@@ -0,0 +1,158 @@
|
||||
#ifndef OSMIUM_GEOM_COORDINATES_HPP
|
||||
#define OSMIUM_GEOM_COORDINATES_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <iosfwd>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/util/double.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace geom {
|
||||
|
||||
struct Coordinates {
|
||||
|
||||
double x;
|
||||
double y;
|
||||
|
||||
/**
|
||||
* Default constructor creates invalid coordinates.
|
||||
*/
|
||||
Coordinates() noexcept :
|
||||
x(std::numeric_limits<double>::quiet_NaN()),
|
||||
y(std::numeric_limits<double>::quiet_NaN()) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Coordinates from doubles. If any of them is NaN, the
|
||||
* coordinates are invalid.
|
||||
*/
|
||||
explicit Coordinates(double cx, double cy) noexcept :
|
||||
x(cx),
|
||||
y(cy) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Coordinates from a Location. Will throw
|
||||
* osmium::invalid_location if the location is not valid.
|
||||
*/
|
||||
Coordinates(const osmium::Location& location) :
|
||||
x(location.lon()),
|
||||
y(location.lat()) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Coordinates are invalid if they have been default constructed.
|
||||
*/
|
||||
bool valid() const noexcept {
|
||||
return !std::isnan(x) && !std::isnan(y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert coordinates to text and append to given string. If the
|
||||
* coordinate is invalid, the fixed string "invalid" will be
|
||||
* added to the string.
|
||||
*
|
||||
* @param s String to append the coordinates to.
|
||||
* @param infix Character to print between the two coordinates.
|
||||
* @param precision Number of digits after the decimal point the
|
||||
* coordinate will be rounded to.
|
||||
*/
|
||||
void append_to_string(std::string& s, const char infix, int precision) const {
|
||||
if (valid()) {
|
||||
osmium::util::double2string(s, x, precision);
|
||||
s += infix;
|
||||
osmium::util::double2string(s, y, precision);
|
||||
} else {
|
||||
s.append("invalid");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert coordinates to text and append to given string. If the
|
||||
* coordinate is invalid, the fixed string "invalid" will be
|
||||
* added to the string between the prefix and suffix characters.
|
||||
*
|
||||
* @param s String to append the coordinates to.
|
||||
* @param prefix Character to print before the first coordinate.
|
||||
* @param infix Character to print between the two coordinates.
|
||||
* @param suffix Character to print after the second coordinate.
|
||||
* @param precision Number of digits after the decimal point the
|
||||
* coordinate will be rounded to.
|
||||
*/
|
||||
void append_to_string(std::string& s, const char prefix, const char infix, const char suffix, int precision) const {
|
||||
s += prefix;
|
||||
append_to_string(s, infix, precision);
|
||||
s += suffix;
|
||||
}
|
||||
|
||||
}; // struct coordinates
|
||||
|
||||
/**
|
||||
* Check whether two Coordinates are equal. Invalid coordinates are
|
||||
* equal to other invalid coordinates but not equal to any valid
|
||||
* coordinates.
|
||||
*
|
||||
* Because this is comparing floating point values, it might not give
|
||||
* the right result if the coordinates have been the result of some
|
||||
* calculation that introduced rounding errors.
|
||||
*/
|
||||
inline bool operator==(const Coordinates& lhs, const Coordinates& rhs) noexcept {
|
||||
if (!lhs.valid() && !rhs.valid()) {
|
||||
return true;
|
||||
}
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
return lhs.x == rhs.x && lhs.y == rhs.y;
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
inline bool operator!=(const Coordinates& lhs, const Coordinates& rhs) noexcept {
|
||||
return ! operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
template <typename TChar, typename TTraits>
|
||||
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const Coordinates& c) {
|
||||
return out << '(' << c.x << ',' << c.y << ')';
|
||||
}
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_COORDINATES_HPP
|
||||
@@ -0,0 +1,428 @@
|
||||
#ifndef OSMIUM_GEOM_FACTORY_HPP
|
||||
#define OSMIUM_GEOM_FACTORY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/memory/collection.hpp>
|
||||
#include <osmium/memory/item.hpp>
|
||||
#include <osmium/osm/area.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/node_ref_list.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* Exception thrown when an invalid geometry is encountered. An example
|
||||
* would be a linestring with less than two points.
|
||||
*/
|
||||
class geometry_error : public std::runtime_error {
|
||||
|
||||
std::string m_message;
|
||||
osmium::object_id_type m_id;
|
||||
|
||||
public:
|
||||
|
||||
explicit geometry_error(const std::string& message, const char* object_type = "", osmium::object_id_type id = 0) :
|
||||
std::runtime_error(message),
|
||||
m_message(message),
|
||||
m_id(id) {
|
||||
if (m_id != 0) {
|
||||
m_message += " (";
|
||||
m_message += object_type;
|
||||
m_message += "_id=";
|
||||
m_message += std::to_string(m_id);
|
||||
m_message += ")";
|
||||
}
|
||||
}
|
||||
|
||||
void set_id(const char* object_type, osmium::object_id_type id) {
|
||||
if (m_id == 0 && id != 0) {
|
||||
m_message += " (";
|
||||
m_message += object_type;
|
||||
m_message += "_id=";
|
||||
m_message += std::to_string(id);
|
||||
m_message += ")";
|
||||
}
|
||||
m_id = id;
|
||||
}
|
||||
|
||||
osmium::object_id_type id() const noexcept {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
const char* what() const noexcept override {
|
||||
return m_message.c_str();
|
||||
}
|
||||
|
||||
}; // class geometry_error
|
||||
|
||||
/**
|
||||
* @brief Everything related to geometry handling.
|
||||
*/
|
||||
namespace geom {
|
||||
|
||||
/**
|
||||
* Which nodes of a way to use for a linestring.
|
||||
*/
|
||||
enum class use_nodes : bool {
|
||||
unique = true, ///< Remove consecutive nodes with same location.
|
||||
all = false ///< Use all nodes.
|
||||
}; // enum class use_nodes
|
||||
|
||||
/**
|
||||
* Which direction the linestring created from a way
|
||||
* should have.
|
||||
*/
|
||||
enum class direction : bool {
|
||||
backward = true, ///< Linestring has reverse direction.
|
||||
forward = false ///< Linestring has same direction as way.
|
||||
}; // enum class direction
|
||||
|
||||
/**
|
||||
* This pseudo projection just returns its WGS84 input unchanged.
|
||||
* Used as a template parameter if a real projection is not needed.
|
||||
*/
|
||||
class IdentityProjection {
|
||||
|
||||
public:
|
||||
|
||||
Coordinates operator()(osmium::Location location) const {
|
||||
return Coordinates{location.lon(), location.lat()};
|
||||
}
|
||||
|
||||
int epsg() const noexcept {
|
||||
return 4326;
|
||||
}
|
||||
|
||||
std::string proj_string() const {
|
||||
return "+proj=longlat +datum=WGS84 +no_defs";
|
||||
}
|
||||
|
||||
}; // class IdentityProjection
|
||||
|
||||
/**
|
||||
* Geometry factory.
|
||||
*/
|
||||
template <typename TGeomImpl, typename TProjection = IdentityProjection>
|
||||
class GeometryFactory {
|
||||
|
||||
/**
|
||||
* Add all points of an outer or inner ring to a multipolygon.
|
||||
*/
|
||||
void add_points(const osmium::NodeRefList& nodes) {
|
||||
osmium::Location last_location;
|
||||
for (const osmium::NodeRef& node_ref : nodes) {
|
||||
if (last_location != node_ref.location()) {
|
||||
last_location = node_ref.location();
|
||||
m_impl.multipolygon_add_location(m_projection(last_location));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TProjection m_projection;
|
||||
TGeomImpl m_impl;
|
||||
|
||||
public:
|
||||
|
||||
GeometryFactory<TGeomImpl, TProjection>() :
|
||||
m_projection(),
|
||||
m_impl(m_projection.epsg()) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for default initialized projection.
|
||||
*/
|
||||
template <typename... TArgs>
|
||||
explicit GeometryFactory<TGeomImpl, TProjection>(TArgs&&... args) :
|
||||
m_projection(),
|
||||
m_impl(m_projection.epsg(), std::forward<TArgs>(args)...) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for explicitly initialized projection. Note that the
|
||||
* projection is moved into the GeometryFactory.
|
||||
*/
|
||||
template <typename... TArgs>
|
||||
explicit GeometryFactory<TGeomImpl, TProjection>(TProjection&& projection, TArgs&&... args) :
|
||||
m_projection(std::move(projection)),
|
||||
m_impl(m_projection.epsg(), std::forward<TArgs>(args)...) {
|
||||
}
|
||||
|
||||
using projection_type = TProjection;
|
||||
|
||||
using point_type = typename TGeomImpl::point_type;
|
||||
using linestring_type = typename TGeomImpl::linestring_type;
|
||||
using polygon_type = typename TGeomImpl::polygon_type;
|
||||
using multipolygon_type = typename TGeomImpl::multipolygon_type;
|
||||
using ring_type = typename TGeomImpl::ring_type;
|
||||
|
||||
int epsg() const noexcept {
|
||||
return m_projection.epsg();
|
||||
}
|
||||
|
||||
std::string proj_string() const {
|
||||
return m_projection.proj_string();
|
||||
}
|
||||
|
||||
/* Point */
|
||||
|
||||
point_type create_point(const osmium::Location& location) const {
|
||||
return m_impl.make_point(m_projection(location));
|
||||
}
|
||||
|
||||
point_type create_point(const osmium::Node& node) {
|
||||
try {
|
||||
return create_point(node.location());
|
||||
} catch (osmium::geometry_error& e) {
|
||||
e.set_id("node", node.id());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
point_type create_point(const osmium::NodeRef& node_ref) {
|
||||
try {
|
||||
return create_point(node_ref.location());
|
||||
} catch (osmium::geometry_error& e) {
|
||||
e.set_id("node", node_ref.ref());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/* LineString */
|
||||
|
||||
void linestring_start() {
|
||||
m_impl.linestring_start();
|
||||
}
|
||||
|
||||
template <typename TIter>
|
||||
size_t fill_linestring(TIter it, TIter end) {
|
||||
size_t num_points = 0;
|
||||
for (; it != end; ++it, ++num_points) {
|
||||
m_impl.linestring_add_location(m_projection(it->location()));
|
||||
}
|
||||
return num_points;
|
||||
}
|
||||
|
||||
template <typename TIter>
|
||||
size_t fill_linestring_unique(TIter it, TIter end) {
|
||||
size_t num_points = 0;
|
||||
osmium::Location last_location;
|
||||
for (; it != end; ++it) {
|
||||
if (last_location != it->location()) {
|
||||
last_location = it->location();
|
||||
m_impl.linestring_add_location(m_projection(last_location));
|
||||
++num_points;
|
||||
}
|
||||
}
|
||||
return num_points;
|
||||
}
|
||||
|
||||
linestring_type linestring_finish(size_t num_points) {
|
||||
return m_impl.linestring_finish(num_points);
|
||||
}
|
||||
|
||||
linestring_type create_linestring(const osmium::WayNodeList& wnl, use_nodes un = use_nodes::unique, direction dir = direction::forward) {
|
||||
linestring_start();
|
||||
size_t num_points = 0;
|
||||
|
||||
if (un == use_nodes::unique) {
|
||||
osmium::Location last_location;
|
||||
switch (dir) {
|
||||
case direction::forward:
|
||||
num_points = fill_linestring_unique(wnl.cbegin(), wnl.cend());
|
||||
break;
|
||||
case direction::backward:
|
||||
num_points = fill_linestring_unique(wnl.crbegin(), wnl.crend());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (dir) {
|
||||
case direction::forward:
|
||||
num_points = fill_linestring(wnl.cbegin(), wnl.cend());
|
||||
break;
|
||||
case direction::backward:
|
||||
num_points = fill_linestring(wnl.crbegin(), wnl.crend());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_points < 2) {
|
||||
throw osmium::geometry_error{"need at least two points for linestring"};
|
||||
}
|
||||
|
||||
return linestring_finish(num_points);
|
||||
}
|
||||
|
||||
linestring_type create_linestring(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir = direction::forward) {
|
||||
try {
|
||||
return create_linestring(way.nodes(), un, dir);
|
||||
} catch (osmium::geometry_error& e) {
|
||||
e.set_id("way", way.id());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/* Polygon */
|
||||
|
||||
void polygon_start() {
|
||||
m_impl.polygon_start();
|
||||
}
|
||||
|
||||
template <typename TIter>
|
||||
size_t fill_polygon(TIter it, TIter end) {
|
||||
size_t num_points = 0;
|
||||
for (; it != end; ++it, ++num_points) {
|
||||
m_impl.polygon_add_location(m_projection(it->location()));
|
||||
}
|
||||
return num_points;
|
||||
}
|
||||
|
||||
template <typename TIter>
|
||||
size_t fill_polygon_unique(TIter it, TIter end) {
|
||||
size_t num_points = 0;
|
||||
osmium::Location last_location;
|
||||
for (; it != end; ++it) {
|
||||
if (last_location != it->location()) {
|
||||
last_location = it->location();
|
||||
m_impl.polygon_add_location(m_projection(last_location));
|
||||
++num_points;
|
||||
}
|
||||
}
|
||||
return num_points;
|
||||
}
|
||||
|
||||
polygon_type polygon_finish(size_t num_points) {
|
||||
return m_impl.polygon_finish(num_points);
|
||||
}
|
||||
|
||||
polygon_type create_polygon(const osmium::WayNodeList& wnl, use_nodes un = use_nodes::unique, direction dir = direction::forward) {
|
||||
polygon_start();
|
||||
size_t num_points = 0;
|
||||
|
||||
if (un == use_nodes::unique) {
|
||||
osmium::Location last_location;
|
||||
switch (dir) {
|
||||
case direction::forward:
|
||||
num_points = fill_polygon_unique(wnl.cbegin(), wnl.cend());
|
||||
break;
|
||||
case direction::backward:
|
||||
num_points = fill_polygon_unique(wnl.crbegin(), wnl.crend());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (dir) {
|
||||
case direction::forward:
|
||||
num_points = fill_polygon(wnl.cbegin(), wnl.cend());
|
||||
break;
|
||||
case direction::backward:
|
||||
num_points = fill_polygon(wnl.crbegin(), wnl.crend());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_points < 4) {
|
||||
throw osmium::geometry_error{"need at least four points for polygon"};
|
||||
}
|
||||
|
||||
return polygon_finish(num_points);
|
||||
}
|
||||
|
||||
polygon_type create_polygon(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir = direction::forward) {
|
||||
try {
|
||||
return create_polygon(way.nodes(), un, dir);
|
||||
} catch (osmium::geometry_error& e) {
|
||||
e.set_id("way", way.id());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/* MultiPolygon */
|
||||
|
||||
multipolygon_type create_multipolygon(const osmium::Area& area) {
|
||||
try {
|
||||
size_t num_polygons = 0;
|
||||
size_t num_rings = 0;
|
||||
m_impl.multipolygon_start();
|
||||
|
||||
for (const auto& item : area) {
|
||||
if (item.type() == osmium::item_type::outer_ring) {
|
||||
auto& ring = static_cast<const osmium::OuterRing&>(item);
|
||||
if (num_polygons > 0) {
|
||||
m_impl.multipolygon_polygon_finish();
|
||||
}
|
||||
m_impl.multipolygon_polygon_start();
|
||||
m_impl.multipolygon_outer_ring_start();
|
||||
add_points(ring);
|
||||
m_impl.multipolygon_outer_ring_finish();
|
||||
++num_rings;
|
||||
++num_polygons;
|
||||
} else if (item.type() == osmium::item_type::inner_ring) {
|
||||
auto& ring = static_cast<const osmium::InnerRing&>(item);
|
||||
m_impl.multipolygon_inner_ring_start();
|
||||
add_points(ring);
|
||||
m_impl.multipolygon_inner_ring_finish();
|
||||
++num_rings;
|
||||
}
|
||||
}
|
||||
|
||||
// if there are no rings, this area is invalid
|
||||
if (num_rings == 0) {
|
||||
throw osmium::geometry_error{"invalid area"};
|
||||
}
|
||||
|
||||
m_impl.multipolygon_polygon_finish();
|
||||
return m_impl.multipolygon_finish();
|
||||
} catch (osmium::geometry_error& e) {
|
||||
e.set_id("area", area.id());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
}; // class GeometryFactory
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_FACTORY_HPP
|
||||
@@ -0,0 +1,161 @@
|
||||
#ifndef OSMIUM_GEOM_GEOJSON_HPP
|
||||
#define OSMIUM_GEOM_GEOJSON_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/geom/factory.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace geom {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class GeoJSONFactoryImpl {
|
||||
|
||||
std::string m_str;
|
||||
int m_precision;
|
||||
|
||||
public:
|
||||
|
||||
using point_type = std::string;
|
||||
using linestring_type = std::string;
|
||||
using polygon_type = std::string;
|
||||
using multipolygon_type = std::string;
|
||||
using ring_type = std::string;
|
||||
|
||||
GeoJSONFactoryImpl(int /* srid */, int precision = 7) :
|
||||
m_precision(precision) {
|
||||
}
|
||||
|
||||
/* Point */
|
||||
|
||||
// { "type": "Point", "coordinates": [100.0, 0.0] }
|
||||
point_type make_point(const osmium::geom::Coordinates& xy) const {
|
||||
std::string str {"{\"type\":\"Point\",\"coordinates\":"};
|
||||
xy.append_to_string(str, '[', ',', ']', m_precision);
|
||||
str += "}";
|
||||
return str;
|
||||
}
|
||||
|
||||
/* LineString */
|
||||
|
||||
// { "type": "LineString", "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] }
|
||||
void linestring_start() {
|
||||
m_str = "{\"type\":\"LineString\",\"coordinates\":[";
|
||||
}
|
||||
|
||||
void linestring_add_location(const osmium::geom::Coordinates& xy) {
|
||||
xy.append_to_string(m_str, '[', ',', ']', m_precision);
|
||||
m_str += ',';
|
||||
}
|
||||
|
||||
linestring_type linestring_finish(size_t /* num_points */) {
|
||||
assert(!m_str.empty());
|
||||
std::string str;
|
||||
|
||||
using std::swap;
|
||||
swap(str, m_str);
|
||||
|
||||
str.back() = ']';
|
||||
str += "}";
|
||||
return str;
|
||||
}
|
||||
|
||||
/* MultiPolygon */
|
||||
|
||||
void multipolygon_start() {
|
||||
m_str = "{\"type\":\"MultiPolygon\",\"coordinates\":[";
|
||||
}
|
||||
|
||||
void multipolygon_polygon_start() {
|
||||
m_str += '[';
|
||||
}
|
||||
|
||||
void multipolygon_polygon_finish() {
|
||||
m_str += "],";
|
||||
}
|
||||
|
||||
void multipolygon_outer_ring_start() {
|
||||
m_str += '[';
|
||||
}
|
||||
|
||||
void multipolygon_outer_ring_finish() {
|
||||
assert(!m_str.empty());
|
||||
m_str.back() = ']';
|
||||
}
|
||||
|
||||
void multipolygon_inner_ring_start() {
|
||||
m_str += ",[";
|
||||
}
|
||||
|
||||
void multipolygon_inner_ring_finish() {
|
||||
assert(!m_str.empty());
|
||||
m_str.back() = ']';
|
||||
}
|
||||
|
||||
void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
|
||||
xy.append_to_string(m_str, '[', ',', ']', m_precision);
|
||||
m_str += ',';
|
||||
}
|
||||
|
||||
multipolygon_type multipolygon_finish() {
|
||||
assert(!m_str.empty());
|
||||
std::string str;
|
||||
|
||||
using std::swap;
|
||||
swap(str, m_str);
|
||||
|
||||
str.back() = ']';
|
||||
str += "}";
|
||||
return str;
|
||||
}
|
||||
|
||||
}; // class GeoJSONFactoryImpl
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename TProjection = IdentityProjection>
|
||||
using GeoJSONFactory = GeometryFactory<osmium::geom::detail::GeoJSONFactoryImpl, TProjection>;
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_GEOJSON_HPP
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
#ifndef OSMIUM_GEOM_GEOS_HPP
|
||||
#define OSMIUM_GEOM_GEOS_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <geos/version.h>
|
||||
#if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && (GEOS_VERSION_MAJOR < 3 || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR <= 5))
|
||||
|
||||
#define OSMIUM_WITH_GEOS
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* This file contains code for conversion of OSM geometries into GEOS
|
||||
* geometries.
|
||||
*
|
||||
* Note that everything in this file is deprecated and only works up to
|
||||
* GEOS 3.5. It uses the GEOS C++ API which the GEOS project does not consider
|
||||
* to be a stable, external API. We probably should have used the GEOS C API
|
||||
* instead.
|
||||
*
|
||||
* @attention If you include this file, you'll need to link with `libgeos`.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <geos/geom/Coordinate.h>
|
||||
#include <geos/geom/CoordinateSequence.h>
|
||||
#include <geos/geom/CoordinateSequenceFactory.h>
|
||||
#include <geos/geom/GeometryFactory.h>
|
||||
#include <geos/geom/LinearRing.h>
|
||||
#include <geos/geom/MultiPolygon.h>
|
||||
#include <geos/geom/Point.h>
|
||||
#include <geos/geom/Polygon.h>
|
||||
#include <geos/geom/PrecisionModel.h>
|
||||
#include <geos/util/GEOSException.h>
|
||||
|
||||
#include <osmium/geom/factory.hpp>
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/util/compatibility.hpp>
|
||||
|
||||
// MSVC doesn't support throw_with_nested yet
|
||||
#ifdef _MSC_VER
|
||||
# define THROW throw
|
||||
#else
|
||||
# include <exception>
|
||||
# define THROW std::throw_with_nested
|
||||
#endif
|
||||
|
||||
namespace osmium {
|
||||
|
||||
struct geos_geometry_error : public geometry_error {
|
||||
|
||||
explicit geos_geometry_error(const char* message) :
|
||||
geometry_error(std::string{"geometry creation failed in GEOS library: "} + message) {
|
||||
}
|
||||
|
||||
}; // struct geos_geometry_error
|
||||
|
||||
namespace geom {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// @deprecated
|
||||
class GEOSFactoryImpl {
|
||||
|
||||
std::unique_ptr<const geos::geom::PrecisionModel> m_precision_model;
|
||||
std::unique_ptr<geos::geom::GeometryFactory> m_our_geos_factory;
|
||||
geos::geom::GeometryFactory* m_geos_factory;
|
||||
|
||||
std::unique_ptr<geos::geom::CoordinateSequence> m_coordinate_sequence;
|
||||
std::vector<std::unique_ptr<geos::geom::LinearRing>> m_rings;
|
||||
std::vector<std::unique_ptr<geos::geom::Polygon>> m_polygons;
|
||||
|
||||
public:
|
||||
|
||||
using point_type = std::unique_ptr<geos::geom::Point>;
|
||||
using linestring_type = std::unique_ptr<geos::geom::LineString>;
|
||||
using polygon_type = std::unique_ptr<geos::geom::Polygon>;
|
||||
using multipolygon_type = std::unique_ptr<geos::geom::MultiPolygon>;
|
||||
using ring_type = std::unique_ptr<geos::geom::LinearRing>;
|
||||
|
||||
explicit GEOSFactoryImpl(int /* srid */, geos::geom::GeometryFactory& geos_factory) :
|
||||
m_precision_model(nullptr),
|
||||
m_our_geos_factory(nullptr),
|
||||
m_geos_factory(&geos_factory) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Do not set SRID explicitly. It will be set to the
|
||||
* correct value automatically.
|
||||
*/
|
||||
OSMIUM_DEPRECATED explicit GEOSFactoryImpl(int /* srid */, int srid) :
|
||||
m_precision_model(new geos::geom::PrecisionModel),
|
||||
m_our_geos_factory(new geos::geom::GeometryFactory{m_precision_model.get(), srid}),
|
||||
m_geos_factory(m_our_geos_factory.get()) {
|
||||
}
|
||||
|
||||
explicit GEOSFactoryImpl(int srid) :
|
||||
m_precision_model(new geos::geom::PrecisionModel),
|
||||
m_our_geos_factory(new geos::geom::GeometryFactory{m_precision_model.get(), srid}),
|
||||
m_geos_factory(m_our_geos_factory.get()) {
|
||||
}
|
||||
|
||||
/* Point */
|
||||
|
||||
point_type make_point(const osmium::geom::Coordinates& xy) const {
|
||||
try {
|
||||
return point_type{m_geos_factory->createPoint(geos::geom::Coordinate{xy.x, xy.y})};
|
||||
} catch (const geos::util::GEOSException& e) {
|
||||
THROW(osmium::geos_geometry_error(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
/* LineString */
|
||||
|
||||
void linestring_start() {
|
||||
try {
|
||||
m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast<std::size_t>(0), 2));
|
||||
} catch (const geos::util::GEOSException& e) {
|
||||
THROW(osmium::geos_geometry_error(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void linestring_add_location(const osmium::geom::Coordinates& xy) {
|
||||
try {
|
||||
m_coordinate_sequence->add(geos::geom::Coordinate{xy.x, xy.y});
|
||||
} catch (const geos::util::GEOSException& e) {
|
||||
THROW(osmium::geos_geometry_error(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
linestring_type linestring_finish(std::size_t /* num_points */) {
|
||||
try {
|
||||
return linestring_type{m_geos_factory->createLineString(m_coordinate_sequence.release())};
|
||||
} catch (const geos::util::GEOSException& e) {
|
||||
THROW(osmium::geos_geometry_error(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
/* MultiPolygon */
|
||||
|
||||
void multipolygon_start() {
|
||||
m_polygons.clear();
|
||||
}
|
||||
|
||||
void multipolygon_polygon_start() {
|
||||
m_rings.clear();
|
||||
}
|
||||
|
||||
void multipolygon_polygon_finish() {
|
||||
try {
|
||||
assert(!m_rings.empty());
|
||||
auto inner_rings = new std::vector<geos::geom::Geometry*>;
|
||||
std::transform(std::next(m_rings.begin(), 1), m_rings.end(), std::back_inserter(*inner_rings), [](std::unique_ptr<geos::geom::LinearRing>& r) {
|
||||
return r.release();
|
||||
});
|
||||
m_polygons.emplace_back(m_geos_factory->createPolygon(m_rings[0].release(), inner_rings));
|
||||
m_rings.clear();
|
||||
} catch (const geos::util::GEOSException& e) {
|
||||
THROW(osmium::geos_geometry_error(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void multipolygon_outer_ring_start() {
|
||||
try {
|
||||
m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast<std::size_t>(0), 2));
|
||||
} catch (const geos::util::GEOSException& e) {
|
||||
THROW(osmium::geos_geometry_error(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void multipolygon_outer_ring_finish() {
|
||||
try {
|
||||
m_rings.emplace_back(m_geos_factory->createLinearRing(m_coordinate_sequence.release()));
|
||||
} catch (const geos::util::GEOSException& e) {
|
||||
THROW(osmium::geos_geometry_error(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void multipolygon_inner_ring_start() {
|
||||
try {
|
||||
m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast<std::size_t>(0), 2));
|
||||
} catch (const geos::util::GEOSException& e) {
|
||||
THROW(osmium::geos_geometry_error(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void multipolygon_inner_ring_finish() {
|
||||
try {
|
||||
m_rings.emplace_back(m_geos_factory->createLinearRing(m_coordinate_sequence.release()));
|
||||
} catch (const geos::util::GEOSException& e) {
|
||||
THROW(osmium::geos_geometry_error(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
|
||||
try {
|
||||
m_coordinate_sequence->add(geos::geom::Coordinate{xy.x, xy.y});
|
||||
} catch (const geos::util::GEOSException& e) {
|
||||
THROW(osmium::geos_geometry_error(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
multipolygon_type multipolygon_finish() {
|
||||
try {
|
||||
auto polygons = new std::vector<geos::geom::Geometry*>;
|
||||
std::transform(m_polygons.begin(), m_polygons.end(), std::back_inserter(*polygons), [](std::unique_ptr<geos::geom::Polygon>& p) {
|
||||
return p.release();
|
||||
});
|
||||
m_polygons.clear();
|
||||
return multipolygon_type{m_geos_factory->createMultiPolygon(polygons)};
|
||||
} catch (const geos::util::GEOSException& e) {
|
||||
THROW(osmium::geos_geometry_error(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
}; // class GEOSFactoryImpl
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// @deprecated
|
||||
template <typename TProjection = IdentityProjection>
|
||||
using GEOSFactory = GeometryFactory<osmium::geom::detail::GEOSFactoryImpl, TProjection>;
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#undef THROW
|
||||
|
||||
#endif
|
||||
|
||||
#endif // OSMIUM_GEOM_GEOS_HPP
|
||||
@@ -0,0 +1,96 @@
|
||||
#ifndef OSMIUM_GEOM_HAVERSINE_HPP
|
||||
#define OSMIUM_GEOM_HAVERSINE_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/geom/util.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace geom {
|
||||
|
||||
/**
|
||||
* @brief Functions to calculate arc distance on Earth using the haversine formula.
|
||||
*
|
||||
* See http://en.wikipedia.org/wiki/Haversine_formula
|
||||
*
|
||||
* Implementation derived from
|
||||
* http://blog.julien.cayzac.name/2008/10/arc-and-distance-between-two-points-on.html
|
||||
*/
|
||||
namespace haversine {
|
||||
|
||||
/// @brief Earth's quadratic mean radius for WGS84
|
||||
constexpr const double EARTH_RADIUS_IN_METERS = 6372797.560856;
|
||||
|
||||
/**
|
||||
* Calculate distance in meters between two sets of coordinates.
|
||||
*
|
||||
* @pre @code c1.valid() && c2.valid() @endcode
|
||||
*/
|
||||
inline double distance(const osmium::geom::Coordinates& c1, const osmium::geom::Coordinates& c2) {
|
||||
double lonh = sin(deg_to_rad(c1.x - c2.x) * 0.5);
|
||||
lonh *= lonh;
|
||||
double lath = sin(deg_to_rad(c1.y - c2.y) * 0.5);
|
||||
lath *= lath;
|
||||
const double tmp = cos(deg_to_rad(c1.y)) * cos(deg_to_rad(c2.y));
|
||||
return 2.0 * EARTH_RADIUS_IN_METERS * asin(sqrt(lath + tmp*lonh));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate length of way.
|
||||
*/
|
||||
inline double distance(const osmium::WayNodeList& wnl) {
|
||||
double sum_length = 0;
|
||||
|
||||
for (auto it = wnl.begin(); it != wnl.end(); ++it) {
|
||||
if (std::next(it) != wnl.end()) {
|
||||
sum_length += distance(it->location(), std::next(it)->location());
|
||||
}
|
||||
}
|
||||
|
||||
return sum_length;
|
||||
}
|
||||
|
||||
} // namespace haversine
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_HAVERSINE_HPP
|
||||
@@ -0,0 +1,162 @@
|
||||
#ifndef OSMIUM_GEOM_MERCATOR_PROJECTION_HPP
|
||||
#define OSMIUM_GEOM_MERCATOR_PROJECTION_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/geom/util.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace geom {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr double earth_radius_for_epsg3857 = 6378137.0;
|
||||
constexpr double max_coordinate_epsg3857 = 20037508.34;
|
||||
|
||||
constexpr inline double lon_to_x(double lon) {
|
||||
return earth_radius_for_epsg3857 * deg_to_rad(lon);
|
||||
}
|
||||
|
||||
inline double lat_to_y_with_tan(double lat) { // not constexpr because math functions aren't
|
||||
return earth_radius_for_epsg3857 * std::log(std::tan(osmium::geom::PI/4 + deg_to_rad(lat)/2));
|
||||
}
|
||||
|
||||
#ifdef OSMIUM_USE_SLOW_MERCATOR_PROJECTION
|
||||
inline double lat_to_y(double lat) {
|
||||
return lat_to_y_with_tan(lat);
|
||||
}
|
||||
#else
|
||||
|
||||
// This is a much faster implementation than the canonical
|
||||
// implementation using the tan() function. For details
|
||||
// see https://github.com/osmcode/mercator-projection .
|
||||
inline double lat_to_y(double lat) { // not constexpr because math functions aren't
|
||||
if (lat < -78.0 || lat > 78.0) {
|
||||
return lat_to_y_with_tan(lat);
|
||||
}
|
||||
|
||||
return earth_radius_for_epsg3857 *
|
||||
((((((((((-3.1112583378460085319e-23 * lat +
|
||||
2.0465852743943268009e-19) * lat +
|
||||
6.4905282018672673884e-18) * lat +
|
||||
-1.9685447939983315591e-14) * lat +
|
||||
-2.2022588158115104182e-13) * lat +
|
||||
5.1617537365509453239e-10) * lat +
|
||||
2.5380136069803016519e-9) * lat +
|
||||
-5.1448323697228488745e-6) * lat +
|
||||
-9.4888671473357768301e-6) * lat +
|
||||
1.7453292518154191887e-2) * lat)
|
||||
/
|
||||
((((((((((-1.9741136066814230637e-22 * lat +
|
||||
-1.258514031244679556e-20) * lat +
|
||||
4.8141483273572351796e-17) * lat +
|
||||
8.6876090870176172185e-16) * lat +
|
||||
-2.3298743439377541768e-12) * lat +
|
||||
-1.9300094785736130185e-11) * lat +
|
||||
4.3251609106864178231e-8) * lat +
|
||||
1.7301944508516974048e-7) * lat +
|
||||
-3.4554675198786337842e-4) * lat +
|
||||
-5.4367203601085991108e-4) * lat + 1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
constexpr inline double x_to_lon(double x) {
|
||||
return rad_to_deg(x) / earth_radius_for_epsg3857;
|
||||
}
|
||||
|
||||
inline double y_to_lat(double y) { // not constexpr because math functions aren't
|
||||
return rad_to_deg(2 * std::atan(std::exp(y / earth_radius_for_epsg3857)) - osmium::geom::PI/2);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* The maximum latitude that can be projected with the Web Mercator
|
||||
* (EPSG:3857) projection.
|
||||
*/
|
||||
constexpr double MERCATOR_MAX_LAT = 85.0511288;
|
||||
|
||||
/**
|
||||
* Convert the coordinates from WGS84 lon/lat to web mercator.
|
||||
*
|
||||
* @pre @code c.valid() @endcode
|
||||
*/
|
||||
inline Coordinates lonlat_to_mercator(const Coordinates& c) {
|
||||
return Coordinates{detail::lon_to_x(c.x), detail::lat_to_y(c.y)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the coordinates from web mercator to WGS84 lon/lat.
|
||||
*
|
||||
* @pre @code c.valid() @endcode
|
||||
*/
|
||||
inline Coordinates mercator_to_lonlat(const Coordinates& c) {
|
||||
return Coordinates{detail::x_to_lon(c.x), detail::y_to_lat(c.y)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Functor that does projection from WGS84 (EPSG:4326) to "Web
|
||||
* Mercator" (EPSG:3857)
|
||||
*/
|
||||
class MercatorProjection {
|
||||
|
||||
public:
|
||||
|
||||
MercatorProjection() {
|
||||
}
|
||||
|
||||
Coordinates operator()(osmium::Location location) const {
|
||||
return Coordinates{detail::lon_to_x(location.lon()), detail::lat_to_y(location.lat())};
|
||||
}
|
||||
|
||||
int epsg() const noexcept {
|
||||
return 3857;
|
||||
}
|
||||
|
||||
std::string proj_string() const {
|
||||
return "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs";
|
||||
}
|
||||
|
||||
}; // class MercatorProjection
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_MERCATOR_PROJECTION_HPP
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
#ifndef OSMIUM_GEOM_OGR_HPP
|
||||
#define OSMIUM_GEOM_OGR_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* This file contains code for conversion of OSM geometries into OGR
|
||||
* geometries.
|
||||
*
|
||||
* @attention If you include this file, you'll need to link with `libgdal`.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <ogr_geometry.h>
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/geom/factory.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace geom {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class OGRFactoryImpl {
|
||||
|
||||
public:
|
||||
|
||||
using point_type = std::unique_ptr<OGRPoint>;
|
||||
using linestring_type = std::unique_ptr<OGRLineString>;
|
||||
using polygon_type = std::unique_ptr<OGRPolygon>;
|
||||
using multipolygon_type = std::unique_ptr<OGRMultiPolygon>;
|
||||
using ring_type = std::unique_ptr<OGRLinearRing>;
|
||||
|
||||
private:
|
||||
|
||||
linestring_type m_linestring{nullptr};
|
||||
multipolygon_type m_multipolygon{nullptr};
|
||||
polygon_type m_polygon{nullptr};
|
||||
ring_type m_ring{nullptr};
|
||||
|
||||
public:
|
||||
|
||||
explicit OGRFactoryImpl(int /* srid */) {
|
||||
}
|
||||
|
||||
/* Point */
|
||||
|
||||
point_type make_point(const osmium::geom::Coordinates& xy) const {
|
||||
return point_type{new OGRPoint{xy.x, xy.y}};
|
||||
}
|
||||
|
||||
/* LineString */
|
||||
|
||||
void linestring_start() {
|
||||
m_linestring.reset(new OGRLineString{});
|
||||
}
|
||||
|
||||
void linestring_add_location(const osmium::geom::Coordinates& xy) {
|
||||
assert(!!m_linestring);
|
||||
m_linestring->addPoint(xy.x, xy.y);
|
||||
}
|
||||
|
||||
linestring_type linestring_finish(size_t /* num_points */) {
|
||||
assert(!!m_linestring);
|
||||
return std::move(m_linestring);
|
||||
}
|
||||
|
||||
/* Polygon */
|
||||
|
||||
void polygon_start() {
|
||||
m_ring.reset(new OGRLinearRing{});
|
||||
}
|
||||
|
||||
void polygon_add_location(const osmium::geom::Coordinates& xy) {
|
||||
assert(!!m_ring);
|
||||
m_ring->addPoint(xy.x, xy.y);
|
||||
}
|
||||
|
||||
polygon_type polygon_finish(size_t /* num_points */) {
|
||||
auto polygon = std::unique_ptr<OGRPolygon>{new OGRPolygon{}};
|
||||
polygon->addRingDirectly(m_ring.release());
|
||||
return polygon;
|
||||
}
|
||||
|
||||
/* MultiPolygon */
|
||||
|
||||
void multipolygon_start() {
|
||||
m_multipolygon.reset(new OGRMultiPolygon{});
|
||||
}
|
||||
|
||||
void multipolygon_polygon_start() {
|
||||
m_polygon.reset(new OGRPolygon{});
|
||||
}
|
||||
|
||||
void multipolygon_polygon_finish() {
|
||||
assert(!!m_multipolygon);
|
||||
assert(!!m_polygon);
|
||||
m_multipolygon->addGeometryDirectly(m_polygon.release());
|
||||
}
|
||||
|
||||
void multipolygon_outer_ring_start() {
|
||||
m_ring.reset(new OGRLinearRing{});
|
||||
}
|
||||
|
||||
void multipolygon_outer_ring_finish() {
|
||||
assert(!!m_polygon);
|
||||
assert(!!m_ring);
|
||||
m_polygon->addRingDirectly(m_ring.release());
|
||||
}
|
||||
|
||||
void multipolygon_inner_ring_start() {
|
||||
m_ring.reset(new OGRLinearRing{});
|
||||
}
|
||||
|
||||
void multipolygon_inner_ring_finish() {
|
||||
assert(!!m_polygon);
|
||||
assert(!!m_ring);
|
||||
m_polygon->addRingDirectly(m_ring.release());
|
||||
}
|
||||
|
||||
void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
|
||||
assert(!!m_polygon);
|
||||
assert(!!m_ring);
|
||||
m_ring->addPoint(xy.x, xy.y);
|
||||
}
|
||||
|
||||
multipolygon_type multipolygon_finish() {
|
||||
assert(!!m_multipolygon);
|
||||
return std::move(m_multipolygon);
|
||||
}
|
||||
|
||||
}; // class OGRFactoryImpl
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename TProjection = IdentityProjection>
|
||||
using OGRFactory = GeometryFactory<osmium::geom::detail::OGRFactoryImpl, TProjection>;
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_OGR_HPP
|
||||
@@ -0,0 +1,193 @@
|
||||
#ifndef OSMIUM_GEOM_PROJECTION_HPP
|
||||
#define OSMIUM_GEOM_PROJECTION_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* This file contains code for projecting OSM locations to arbitrary
|
||||
* coordinate reference systems. It is based on the Proj.4 library.
|
||||
*
|
||||
* @attention If you include this file, you'll need to link with `libproj`.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <proj_api.h>
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/geom/mercator_projection.hpp>
|
||||
#include <osmium/geom/util.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace geom {
|
||||
|
||||
/**
|
||||
* C++ wrapper for a Coordinate Reference System of the proj library.
|
||||
*/
|
||||
class CRS {
|
||||
|
||||
struct ProjCRSDeleter {
|
||||
void operator()(void* crs) {
|
||||
pj_free(crs);
|
||||
}
|
||||
}; // struct ProjCRSDeleter
|
||||
|
||||
std::unique_ptr<void, ProjCRSDeleter> m_crs;
|
||||
|
||||
public:
|
||||
|
||||
explicit CRS(const char* crs) :
|
||||
m_crs(pj_init_plus(crs), ProjCRSDeleter()) {
|
||||
if (!m_crs) {
|
||||
throw osmium::projection_error{std::string{"creation of CRS failed: "} + pj_strerrno(*pj_get_errno_ref())};
|
||||
}
|
||||
}
|
||||
|
||||
explicit CRS(const std::string& crs) :
|
||||
CRS(crs.c_str()) {
|
||||
}
|
||||
|
||||
explicit CRS(int epsg) :
|
||||
CRS(std::string{"+init=epsg:"} + std::to_string(epsg)) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get underlying projPJ handle from proj library.
|
||||
*/
|
||||
projPJ get() const noexcept {
|
||||
return m_crs.get();
|
||||
}
|
||||
|
||||
bool is_latlong() const noexcept {
|
||||
return pj_is_latlong(m_crs.get()) != 0;
|
||||
}
|
||||
|
||||
bool is_geocent() const noexcept {
|
||||
return pj_is_geocent(m_crs.get()) != 0;
|
||||
}
|
||||
|
||||
}; // class CRS
|
||||
|
||||
/**
|
||||
* Transform coordinates from one CRS into another. Wraps the same
|
||||
* function of the proj library.
|
||||
*
|
||||
* Coordinates have to be in radians and are produced in radians.
|
||||
*
|
||||
* @throws osmium::projection_error if the projection fails
|
||||
*/
|
||||
// cppcheck-suppress passedByValue (because c is small and we want to change it)
|
||||
inline Coordinates transform(const CRS& src, const CRS& dest, Coordinates c) {
|
||||
const int result = pj_transform(src.get(), dest.get(), 1, 1, &c.x, &c.y, nullptr);
|
||||
if (result != 0) {
|
||||
throw osmium::projection_error{std::string{"projection failed: "} + pj_strerrno(result)};
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Functor that does projection from WGS84 (EPSG:4326) to the given
|
||||
* CRS.
|
||||
*
|
||||
* If this Projection is initialized with the constructor taking
|
||||
* an integer with the epsg code 4326, no projection is done. If it
|
||||
* is initialized with epsg code 3857 the Osmium-internal
|
||||
* implementation of the Mercator projection is used, otherwise this
|
||||
* falls back to using the proj.4 library. Note that this "magic" does
|
||||
* not work if you use any of the constructors taking a string.
|
||||
*/
|
||||
class Projection {
|
||||
|
||||
int m_epsg;
|
||||
std::string m_proj_string;
|
||||
CRS m_crs_wgs84{4326};
|
||||
CRS m_crs_user;
|
||||
|
||||
public:
|
||||
|
||||
explicit Projection(const std::string& proj_string) :
|
||||
m_epsg(-1),
|
||||
m_proj_string(proj_string),
|
||||
m_crs_user(proj_string) {
|
||||
}
|
||||
|
||||
explicit Projection(const char* proj_string) :
|
||||
m_epsg(-1),
|
||||
m_proj_string(proj_string),
|
||||
m_crs_user(proj_string) {
|
||||
}
|
||||
|
||||
explicit Projection(int epsg) :
|
||||
m_epsg(epsg),
|
||||
m_proj_string(std::string{"+init=epsg:"} + std::to_string(epsg)),
|
||||
m_crs_user(epsg) {
|
||||
}
|
||||
|
||||
Coordinates operator()(osmium::Location location) const {
|
||||
if (m_epsg == 4326) {
|
||||
return Coordinates{location.lon(), location.lat()};
|
||||
} else if (m_epsg == 3857) {
|
||||
return Coordinates{detail::lon_to_x(location.lon()),
|
||||
detail::lat_to_y(location.lat())};
|
||||
}
|
||||
|
||||
Coordinates c{transform(m_crs_wgs84, m_crs_user, Coordinates{deg_to_rad(location.lon()),
|
||||
deg_to_rad(location.lat())})};
|
||||
if (m_crs_user.is_latlong()) {
|
||||
c.x = rad_to_deg(c.x);
|
||||
c.y = rad_to_deg(c.y);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int epsg() const noexcept {
|
||||
return m_epsg;
|
||||
}
|
||||
|
||||
std::string proj_string() const {
|
||||
return m_proj_string;
|
||||
}
|
||||
|
||||
}; // class Projection
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_PROJECTION_HPP
|
||||
@@ -0,0 +1,192 @@
|
||||
#ifndef OSMIUM_GEOM_RAPID_GEOJSON_HPP
|
||||
#define OSMIUM_GEOM_RAPID_GEOJSON_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/geom/factory.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace geom {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* A geometry factory implementation that can be used with the
|
||||
* RapidJSON (https://github.com/miloyip/rapidjson) JSON writer.
|
||||
*/
|
||||
template <typename TWriter>
|
||||
class RapidGeoJSONFactoryImpl {
|
||||
|
||||
TWriter* m_writer;
|
||||
|
||||
public:
|
||||
|
||||
using point_type = void;
|
||||
using linestring_type = void;
|
||||
using polygon_type = void;
|
||||
using multipolygon_type = void;
|
||||
using ring_type = void;
|
||||
|
||||
RapidGeoJSONFactoryImpl(int /* srid */, TWriter& writer) :
|
||||
m_writer(&writer) {
|
||||
}
|
||||
|
||||
/* Point */
|
||||
|
||||
// { "type": "Point", "coordinates": [100.0, 0.0] }
|
||||
point_type make_point(const osmium::geom::Coordinates& xy) const {
|
||||
m_writer->String("geometry");
|
||||
m_writer->StartObject();
|
||||
m_writer->String("type");
|
||||
m_writer->String("Point");
|
||||
m_writer->String("coordinates");
|
||||
m_writer->StartArray();
|
||||
m_writer->Double(xy.x);
|
||||
m_writer->Double(xy.y);
|
||||
m_writer->EndArray();
|
||||
m_writer->EndObject();
|
||||
}
|
||||
|
||||
/* LineString */
|
||||
|
||||
// { "type": "LineString", "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] }
|
||||
void linestring_start() {
|
||||
m_writer->String("geometry");
|
||||
m_writer->StartObject();
|
||||
m_writer->String("type");
|
||||
m_writer->String("LineString");
|
||||
m_writer->String("coordinates");
|
||||
m_writer->StartArray();
|
||||
}
|
||||
|
||||
void linestring_add_location(const osmium::geom::Coordinates& xy) {
|
||||
m_writer->StartArray();
|
||||
m_writer->Double(xy.x);
|
||||
m_writer->Double(xy.y);
|
||||
m_writer->EndArray();
|
||||
}
|
||||
|
||||
linestring_type linestring_finish(size_t /* num_points */) {
|
||||
m_writer->EndArray();
|
||||
m_writer->EndObject();
|
||||
}
|
||||
|
||||
/* Polygon */
|
||||
|
||||
// { "type": "Polygon", "coordinates": [[[100.0, 0.0], [101.0, 1.0]]] }
|
||||
void polygon_start() {
|
||||
m_writer->String("geometry");
|
||||
m_writer->StartObject();
|
||||
m_writer->String("type");
|
||||
m_writer->String("Polygon");
|
||||
m_writer->String("coordinates");
|
||||
m_writer->StartArray();
|
||||
m_writer->StartArray();
|
||||
}
|
||||
|
||||
void polygon_add_location(const osmium::geom::Coordinates& xy) {
|
||||
m_writer->StartArray();
|
||||
m_writer->Double(xy.x);
|
||||
m_writer->Double(xy.y);
|
||||
m_writer->EndArray();
|
||||
}
|
||||
|
||||
polygon_type polygon_finish(size_t /* num_points */) {
|
||||
m_writer->EndArray();
|
||||
m_writer->EndArray();
|
||||
m_writer->EndObject();
|
||||
}
|
||||
|
||||
/* MultiPolygon */
|
||||
|
||||
void multipolygon_start() {
|
||||
m_writer->String("geometry");
|
||||
m_writer->StartObject();
|
||||
m_writer->String("type");
|
||||
m_writer->String("MultiPolygon");
|
||||
m_writer->String("coordinates");
|
||||
m_writer->StartArray();
|
||||
}
|
||||
|
||||
void multipolygon_polygon_start() {
|
||||
m_writer->StartArray();
|
||||
}
|
||||
|
||||
void multipolygon_polygon_finish() {
|
||||
m_writer->EndArray();
|
||||
}
|
||||
|
||||
void multipolygon_outer_ring_start() {
|
||||
m_writer->StartArray();
|
||||
}
|
||||
|
||||
void multipolygon_outer_ring_finish() {
|
||||
m_writer->EndArray();
|
||||
}
|
||||
|
||||
void multipolygon_inner_ring_start() {
|
||||
m_writer->StartArray();
|
||||
}
|
||||
|
||||
void multipolygon_inner_ring_finish() {
|
||||
m_writer->EndArray();
|
||||
}
|
||||
|
||||
void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
|
||||
m_writer->StartArray();
|
||||
m_writer->Double(xy.x);
|
||||
m_writer->Double(xy.y);
|
||||
m_writer->EndArray();
|
||||
}
|
||||
|
||||
multipolygon_type multipolygon_finish() {
|
||||
m_writer->EndArray();
|
||||
m_writer->EndObject();
|
||||
}
|
||||
|
||||
}; // class RapidGeoJSONFactoryImpl
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename TWriter, typename TProjection = IdentityProjection>
|
||||
using RapidGeoJSONFactory = GeometryFactory<detail::RapidGeoJSONFactoryImpl<TWriter>, TProjection>;
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_RAPID_GEOJSON_HPP
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef OSMIUM_GEOM_RELATIONS_HPP
|
||||
#define OSMIUM_GEOM_RELATIONS_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace geom {
|
||||
|
||||
/**
|
||||
* Check whether one geometry contains another.
|
||||
*/
|
||||
inline bool contains(const osmium::Box& lhs, const osmium::Box& rhs) noexcept {
|
||||
return ((lhs.bottom_left().x() >= rhs.bottom_left().x()) &&
|
||||
(lhs.top_right().x() <= rhs.top_right().x()) &&
|
||||
(lhs.bottom_left().y() >= rhs.bottom_left().y()) &&
|
||||
(lhs.top_right().y() <= rhs.top_right().y()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether one geometry overlaps another.
|
||||
*/
|
||||
inline bool overlaps(const osmium::Box& lhs, const osmium::Box& rhs) noexcept {
|
||||
return ((lhs.bottom_left().x() <= rhs.top_right().x()) &&
|
||||
(lhs.bottom_left().y() <= rhs.top_right().y()) &&
|
||||
(rhs.bottom_left().x() <= lhs.top_right().x()) &&
|
||||
(rhs.bottom_left().y() <= lhs.top_right().y()));
|
||||
}
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_RELATIONS_HPP
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
#ifndef OSMIUM_GEOM_TILE_HPP
|
||||
#define OSMIUM_GEOM_TILE_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/geom/mercator_projection.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace geom {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
inline constexpr const T& clamp(const T& value, const T& min, const T& max) {
|
||||
return value < min ? min : (max < value ? max : value);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Returns the number of tiles (in each direction) for the given zoom
|
||||
* level.
|
||||
*/
|
||||
inline constexpr uint32_t num_tiles_in_zoom(uint32_t zoom) noexcept {
|
||||
return 1u << zoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width or height of a tile in web mercator coordinates for
|
||||
* the given zoom level.
|
||||
*/
|
||||
inline constexpr double tile_extent_in_zoom(uint32_t zoom) noexcept {
|
||||
return detail::max_coordinate_epsg3857 * 2 / num_tiles_in_zoom(zoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tile x number from an x coordinate in web mercator
|
||||
* projection in the given zoom level. Tiles are numbered from left
|
||||
* to right.
|
||||
*/
|
||||
inline constexpr uint32_t mercx_to_tilex(uint32_t zoom, double x) noexcept {
|
||||
return static_cast<uint32_t>(detail::clamp<int32_t>(
|
||||
static_cast<int32_t>((x + detail::max_coordinate_epsg3857) / tile_extent_in_zoom(zoom)),
|
||||
0, num_tiles_in_zoom(zoom) -1
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tile y number from an y coordinate in web mercator
|
||||
* projection in the given zoom level. Tiles are numbered from top
|
||||
* to bottom.
|
||||
*/
|
||||
inline constexpr uint32_t mercy_to_tiley(uint32_t zoom, double y) noexcept {
|
||||
return static_cast<uint32_t>(detail::clamp<int32_t>(
|
||||
static_cast<int32_t>((detail::max_coordinate_epsg3857 - y) / tile_extent_in_zoom(zoom)),
|
||||
0, num_tiles_in_zoom(zoom) -1
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* A tile in the usual Mercator projection.
|
||||
*/
|
||||
struct Tile {
|
||||
|
||||
/// x coordinate
|
||||
uint32_t x;
|
||||
|
||||
/// y coordinate
|
||||
uint32_t y;
|
||||
|
||||
/// Zoom level
|
||||
uint32_t z;
|
||||
|
||||
/**
|
||||
* Create a tile with the given zoom level and x and y tile
|
||||
* coordinates.
|
||||
*
|
||||
* The values are not checked for validity.
|
||||
*
|
||||
* @pre @code zoom <= 30 && x < 2^zoom && y < 2^zoom @endcode
|
||||
*/
|
||||
explicit Tile(uint32_t zoom, uint32_t tx, uint32_t ty) noexcept :
|
||||
x(tx),
|
||||
y(ty),
|
||||
z(zoom) {
|
||||
assert(zoom <= 30u);
|
||||
assert(x < num_tiles_in_zoom(zoom));
|
||||
assert(y < num_tiles_in_zoom(zoom));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tile with the given zoom level that contains the given
|
||||
* location.
|
||||
*
|
||||
* The values are not checked for validity.
|
||||
*
|
||||
* @pre @code location.valid() && zoom <= 30 @endcode
|
||||
*/
|
||||
explicit Tile(uint32_t zoom, const osmium::Location& location) :
|
||||
z(zoom) {
|
||||
assert(zoom <= 30u);
|
||||
assert(location.valid());
|
||||
const auto coordinates = lonlat_to_mercator(location);
|
||||
x = mercx_to_tilex(zoom, coordinates.x);
|
||||
y = mercy_to_tiley(zoom, coordinates.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tile with the given zoom level that contains the given
|
||||
* coordinates in Mercator projection.
|
||||
*
|
||||
* The values are not checked for validity.
|
||||
*
|
||||
* @pre @code coordinates.valid() && zoom <= 30 @endcode
|
||||
*/
|
||||
explicit Tile(uint32_t zoom, const osmium::geom::Coordinates& coordinates) :
|
||||
z(zoom) {
|
||||
assert(zoom <= 30u);
|
||||
x = mercx_to_tilex(zoom, coordinates.x);
|
||||
y = mercy_to_tiley(zoom, coordinates.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this tile is valid. For a tile to be valid the
|
||||
* zoom level must be between 0 and 30 and the coordinates must
|
||||
* each be between 0 and 2^zoom-1.
|
||||
*/
|
||||
bool valid() const noexcept {
|
||||
if (z > 30) {
|
||||
return false;
|
||||
}
|
||||
const auto max = num_tiles_in_zoom(z);
|
||||
return x < max && y < max;
|
||||
}
|
||||
|
||||
}; // struct Tile
|
||||
|
||||
/// Tiles are equal if all their attributes are equal.
|
||||
inline bool operator==(const Tile& lhs, const Tile& rhs) noexcept {
|
||||
return lhs.z == rhs.z && lhs.x == rhs.x && lhs.y == rhs.y;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Tile& lhs, const Tile& rhs) noexcept {
|
||||
return ! (lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* This defines an arbitrary order on tiles for use in std::map etc.
|
||||
*/
|
||||
inline bool operator<(const Tile& lhs, const Tile& rhs) noexcept {
|
||||
if (lhs.z < rhs.z) {
|
||||
return true;
|
||||
}
|
||||
if (lhs.z > rhs.z) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.x < rhs.x) {
|
||||
return true;
|
||||
}
|
||||
if (lhs.x > rhs.x) {
|
||||
return false;
|
||||
}
|
||||
return lhs.y < rhs.y;
|
||||
}
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_TILE_HPP
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef OSMIUM_GEOM_UTIL_HPP
|
||||
#define OSMIUM_GEOM_UTIL_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* Exception thrown when a projection object can not be initialized or the
|
||||
* projection of some coordinates can not be calculated.
|
||||
*/
|
||||
struct projection_error : public std::runtime_error {
|
||||
|
||||
explicit projection_error(const std::string& what) :
|
||||
std::runtime_error(what) {
|
||||
}
|
||||
|
||||
explicit projection_error(const char* what) :
|
||||
std::runtime_error(what) {
|
||||
}
|
||||
|
||||
}; // struct projection_error
|
||||
|
||||
namespace geom {
|
||||
|
||||
constexpr double PI = 3.14159265358979323846;
|
||||
|
||||
/// Convert angle from degrees to radians.
|
||||
inline constexpr double deg_to_rad(double degree) noexcept {
|
||||
return degree * (PI / 180.0);
|
||||
}
|
||||
|
||||
/// Convert angle from radians to degrees.
|
||||
inline constexpr double rad_to_deg(double radians) noexcept {
|
||||
return radians * (180.0 / PI);
|
||||
}
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_UTIL_HPP
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
#ifndef OSMIUM_GEOM_WKB_HPP
|
||||
#define OSMIUM_GEOM_WKB_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/geom/factory.hpp>
|
||||
#include <osmium/util/cast.hpp>
|
||||
#include <osmium/util/endian.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace geom {
|
||||
|
||||
enum class wkb_type : bool {
|
||||
wkb = false,
|
||||
ewkb = true
|
||||
}; // enum class wkb_type
|
||||
|
||||
enum class out_type : bool {
|
||||
binary = false,
|
||||
hex = true
|
||||
}; // enum class out_type
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
inline void str_push(std::string& str, T data) {
|
||||
str.append(reinterpret_cast<const char*>(&data), sizeof(T));
|
||||
}
|
||||
|
||||
inline std::string convert_to_hex(const std::string& str) {
|
||||
static const char* lookup_hex = "0123456789ABCDEF";
|
||||
std::string out;
|
||||
out.reserve(str.size() * 2);
|
||||
|
||||
for (char c : str) {
|
||||
out += lookup_hex[(c >> 4) & 0xf];
|
||||
out += lookup_hex[c & 0xf];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
class WKBFactoryImpl {
|
||||
|
||||
/**
|
||||
* Type of WKB geometry.
|
||||
* These definitions are from
|
||||
* 99-049_OpenGIS_Simple_Features_Specification_For_SQL_Rev_1.1.pdf (for WKB)
|
||||
* and http://trac.osgeo.org/postgis/browser/trunk/doc/ZMSgeoms.txt (for EWKB).
|
||||
* They are used to encode geometries into the WKB format.
|
||||
*/
|
||||
enum wkbGeometryType : uint32_t {
|
||||
wkbPoint = 1,
|
||||
wkbLineString = 2,
|
||||
wkbPolygon = 3,
|
||||
wkbMultiPoint = 4,
|
||||
wkbMultiLineString = 5,
|
||||
wkbMultiPolygon = 6,
|
||||
wkbGeometryCollection = 7,
|
||||
|
||||
// SRID-presence flag (EWKB)
|
||||
wkbSRID = 0x20000000
|
||||
}; // enum wkbGeometryType
|
||||
|
||||
/**
|
||||
* Byte order marker in WKB geometry.
|
||||
*/
|
||||
enum class wkb_byte_order_type : uint8_t {
|
||||
XDR = 0, // Big Endian
|
||||
NDR = 1 // Little Endian
|
||||
}; // enum class wkb_byte_order_type
|
||||
|
||||
std::string m_data;
|
||||
uint32_t m_points = 0;
|
||||
int m_srid;
|
||||
wkb_type m_wkb_type;
|
||||
out_type m_out_type;
|
||||
|
||||
std::size_t m_linestring_size_offset = 0;
|
||||
std::size_t m_polygons = 0;
|
||||
std::size_t m_rings = 0;
|
||||
std::size_t m_multipolygon_size_offset = 0;
|
||||
std::size_t m_polygon_size_offset = 0;
|
||||
std::size_t m_ring_size_offset = 0;
|
||||
|
||||
std::size_t header(std::string& str, wkbGeometryType type, bool add_length) const {
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
str_push(str, wkb_byte_order_type::NDR);
|
||||
#else
|
||||
str_push(str, wkb_byte_order_type::XDR);
|
||||
#endif
|
||||
if (m_wkb_type == wkb_type::ewkb) {
|
||||
str_push(str, type | wkbSRID);
|
||||
str_push(str, m_srid);
|
||||
} else {
|
||||
str_push(str, type);
|
||||
}
|
||||
const std::size_t offset = str.size();
|
||||
if (add_length) {
|
||||
str_push(str, static_cast<uint32_t>(0));
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
void set_size(const std::size_t offset, const std::size_t size) {
|
||||
uint32_t s = static_cast_with_assert<uint32_t>(size);
|
||||
std::copy_n(reinterpret_cast<char*>(&s), sizeof(uint32_t), &m_data[offset]);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
using point_type = std::string;
|
||||
using linestring_type = std::string;
|
||||
using polygon_type = std::string;
|
||||
using multipolygon_type = std::string;
|
||||
using ring_type = std::string;
|
||||
|
||||
explicit WKBFactoryImpl(int srid, wkb_type wtype = wkb_type::wkb, out_type otype = out_type::binary) :
|
||||
m_srid(srid),
|
||||
m_wkb_type(wtype),
|
||||
m_out_type(otype) {
|
||||
}
|
||||
|
||||
/* Point */
|
||||
|
||||
point_type make_point(const osmium::geom::Coordinates& xy) const {
|
||||
std::string data;
|
||||
header(data, wkbPoint, false);
|
||||
str_push(data, xy.x);
|
||||
str_push(data, xy.y);
|
||||
|
||||
if (m_out_type == out_type::hex) {
|
||||
return convert_to_hex(data);
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/* LineString */
|
||||
|
||||
void linestring_start() {
|
||||
m_data.clear();
|
||||
m_linestring_size_offset = header(m_data, wkbLineString, true);
|
||||
}
|
||||
|
||||
void linestring_add_location(const osmium::geom::Coordinates& xy) {
|
||||
str_push(m_data, xy.x);
|
||||
str_push(m_data, xy.y);
|
||||
}
|
||||
|
||||
linestring_type linestring_finish(std::size_t num_points) {
|
||||
set_size(m_linestring_size_offset, num_points);
|
||||
std::string data;
|
||||
|
||||
using std::swap;
|
||||
swap(data, m_data);
|
||||
|
||||
if (m_out_type == out_type::hex) {
|
||||
return convert_to_hex(data);
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/* MultiPolygon */
|
||||
|
||||
void multipolygon_start() {
|
||||
m_data.clear();
|
||||
m_polygons = 0;
|
||||
m_multipolygon_size_offset = header(m_data, wkbMultiPolygon, true);
|
||||
}
|
||||
|
||||
void multipolygon_polygon_start() {
|
||||
++m_polygons;
|
||||
m_rings = 0;
|
||||
m_polygon_size_offset = header(m_data, wkbPolygon, true);
|
||||
}
|
||||
|
||||
void multipolygon_polygon_finish() {
|
||||
set_size(m_polygon_size_offset, m_rings);
|
||||
}
|
||||
|
||||
void multipolygon_outer_ring_start() {
|
||||
++m_rings;
|
||||
m_points = 0;
|
||||
m_ring_size_offset = m_data.size();
|
||||
str_push(m_data, static_cast<uint32_t>(0));
|
||||
}
|
||||
|
||||
void multipolygon_outer_ring_finish() {
|
||||
set_size(m_ring_size_offset, m_points);
|
||||
}
|
||||
|
||||
void multipolygon_inner_ring_start() {
|
||||
++m_rings;
|
||||
m_points = 0;
|
||||
m_ring_size_offset = m_data.size();
|
||||
str_push(m_data, static_cast<uint32_t>(0));
|
||||
}
|
||||
|
||||
void multipolygon_inner_ring_finish() {
|
||||
set_size(m_ring_size_offset, m_points);
|
||||
}
|
||||
|
||||
void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
|
||||
str_push(m_data, xy.x);
|
||||
str_push(m_data, xy.y);
|
||||
++m_points;
|
||||
}
|
||||
|
||||
multipolygon_type multipolygon_finish() {
|
||||
set_size(m_multipolygon_size_offset, m_polygons);
|
||||
std::string data;
|
||||
|
||||
using std::swap;
|
||||
swap(data, m_data);
|
||||
|
||||
if (m_out_type == out_type::hex) {
|
||||
return convert_to_hex(data);
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
}; // class WKBFactoryImpl
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename TProjection = IdentityProjection>
|
||||
using WKBFactory = GeometryFactory<osmium::geom::detail::WKBFactoryImpl, TProjection>;
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_WKB_HPP
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
#ifndef OSMIUM_GEOM_WKT_HPP
|
||||
#define OSMIUM_GEOM_WKT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/geom/factory.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace geom {
|
||||
|
||||
enum class wkt_type : bool {
|
||||
wkt = false,
|
||||
ewkt = true
|
||||
}; // enum class wkt_type
|
||||
|
||||
namespace detail {
|
||||
|
||||
class WKTFactoryImpl {
|
||||
|
||||
std::string m_srid_prefix;
|
||||
std::string m_str;
|
||||
int m_precision;
|
||||
wkt_type m_wkt_type;
|
||||
|
||||
public:
|
||||
|
||||
using point_type = std::string;
|
||||
using linestring_type = std::string;
|
||||
using polygon_type = std::string;
|
||||
using multipolygon_type = std::string;
|
||||
using ring_type = std::string;
|
||||
|
||||
WKTFactoryImpl(int srid, int precision = 7, wkt_type wtype = wkt_type::wkt) :
|
||||
m_srid_prefix(),
|
||||
m_precision(precision),
|
||||
m_wkt_type(wtype) {
|
||||
if (m_wkt_type == wkt_type::ewkt) {
|
||||
m_srid_prefix = "SRID=";
|
||||
m_srid_prefix += std::to_string(srid);
|
||||
m_srid_prefix += ';';
|
||||
}
|
||||
}
|
||||
|
||||
/* Point */
|
||||
|
||||
point_type make_point(const osmium::geom::Coordinates& xy) const {
|
||||
std::string str {m_srid_prefix};
|
||||
str += "POINT";
|
||||
xy.append_to_string(str, '(', ' ', ')', m_precision);
|
||||
return str;
|
||||
}
|
||||
|
||||
/* LineString */
|
||||
|
||||
void linestring_start() {
|
||||
m_str = m_srid_prefix;
|
||||
m_str += "LINESTRING(";
|
||||
}
|
||||
|
||||
void linestring_add_location(const osmium::geom::Coordinates& xy) {
|
||||
xy.append_to_string(m_str, ' ', m_precision);
|
||||
m_str += ',';
|
||||
}
|
||||
|
||||
linestring_type linestring_finish(size_t /* num_points */) {
|
||||
assert(!m_str.empty());
|
||||
std::string str;
|
||||
|
||||
using std::swap;
|
||||
swap(str, m_str);
|
||||
|
||||
str.back() = ')';
|
||||
return str;
|
||||
}
|
||||
|
||||
/* MultiPolygon */
|
||||
|
||||
void multipolygon_start() {
|
||||
m_str = m_srid_prefix;
|
||||
m_str += "MULTIPOLYGON(";
|
||||
}
|
||||
|
||||
void multipolygon_polygon_start() {
|
||||
m_str += '(';
|
||||
}
|
||||
|
||||
void multipolygon_polygon_finish() {
|
||||
m_str += "),";
|
||||
}
|
||||
|
||||
void multipolygon_outer_ring_start() {
|
||||
m_str += '(';
|
||||
}
|
||||
|
||||
void multipolygon_outer_ring_finish() {
|
||||
assert(!m_str.empty());
|
||||
m_str.back() = ')';
|
||||
}
|
||||
|
||||
void multipolygon_inner_ring_start() {
|
||||
m_str += ",(";
|
||||
}
|
||||
|
||||
void multipolygon_inner_ring_finish() {
|
||||
assert(!m_str.empty());
|
||||
m_str.back() = ')';
|
||||
}
|
||||
|
||||
void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
|
||||
xy.append_to_string(m_str, ' ', m_precision);
|
||||
m_str += ',';
|
||||
}
|
||||
|
||||
multipolygon_type multipolygon_finish() {
|
||||
assert(!m_str.empty());
|
||||
std::string str;
|
||||
|
||||
using std::swap;
|
||||
swap(str, m_str);
|
||||
|
||||
str.back() = ')';
|
||||
return str;
|
||||
}
|
||||
|
||||
}; // class WKTFactoryImpl
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename TProjection = IdentityProjection>
|
||||
using WKTFactory = GeometryFactory<osmium::geom::detail::WKTFactoryImpl, TProjection>;
|
||||
|
||||
} // namespace geom
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_GEOM_WKT_HPP
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
#ifndef OSMIUM_HANDLER_HPP
|
||||
#define OSMIUM_HANDLER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
namespace osmium {
|
||||
|
||||
class Area;
|
||||
class Changeset;
|
||||
class ChangesetDiscussion;
|
||||
class InnerRing;
|
||||
class Node;
|
||||
class OSMObject;
|
||||
class OuterRing;
|
||||
class Relation;
|
||||
class RelationMemberList;
|
||||
class TagList;
|
||||
class Way;
|
||||
class WayNodeList;
|
||||
|
||||
/**
|
||||
* @brief Osmium handlers provide callbacks for OSM objects
|
||||
*/
|
||||
namespace handler {
|
||||
|
||||
/**
|
||||
* Handler base class. Never used directly. Derive your own class from
|
||||
* this class and "overwrite" the functions. Your functions must be
|
||||
* named the same, but don't have to be const or noexcept or take
|
||||
* their argument as const.
|
||||
*
|
||||
* Usually you will overwrite the node(), way(), and relation()
|
||||
* functions. If your program supports multipolygons, also the area()
|
||||
* function. You can also use the osm_object() function which is
|
||||
* called for all OSM objects (nodes, ways, relations, and areas)
|
||||
* right before each of their specific callbacks is called.
|
||||
*
|
||||
* If you are working with changesets, implement the changeset()
|
||||
* function.
|
||||
*/
|
||||
class Handler {
|
||||
|
||||
public:
|
||||
|
||||
void osm_object(const osmium::OSMObject&) const noexcept {
|
||||
}
|
||||
|
||||
void node(const osmium::Node&) const noexcept {
|
||||
}
|
||||
|
||||
void way(const osmium::Way&) const noexcept {
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation&) const noexcept {
|
||||
}
|
||||
|
||||
void area(const osmium::Area&) const noexcept {
|
||||
}
|
||||
|
||||
void changeset(const osmium::Changeset&) const noexcept {
|
||||
}
|
||||
|
||||
void tag_list(const osmium::TagList&) const noexcept {
|
||||
}
|
||||
|
||||
void way_node_list(const osmium::WayNodeList&) const noexcept {
|
||||
}
|
||||
|
||||
void relation_member_list(const osmium::RelationMemberList&) const noexcept {
|
||||
}
|
||||
|
||||
void outer_ring(const osmium::OuterRing&) const noexcept {
|
||||
}
|
||||
|
||||
void inner_ring(const osmium::InnerRing&) const noexcept {
|
||||
}
|
||||
|
||||
void changeset_discussion(const osmium::ChangesetDiscussion&) const noexcept {
|
||||
}
|
||||
|
||||
void flush() const noexcept {
|
||||
}
|
||||
|
||||
}; // class Handler
|
||||
|
||||
} // namespace handler
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_HANDLER_HPP
|
||||
@@ -0,0 +1,128 @@
|
||||
#ifndef OSMIUM_HANDLER_CHAIN_HPP
|
||||
#define OSMIUM_HANDLER_CHAIN_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include <osmium/handler.hpp>
|
||||
|
||||
#define OSMIUM_CHAIN_HANDLER_CALL(_func_, _type_) \
|
||||
template <int N, int SIZE, typename THandlers> \
|
||||
struct call_ ## _func_ { \
|
||||
void operator()(THandlers& handlers, osmium::_type_& object) { \
|
||||
std::get<N>(handlers)._func_(object); \
|
||||
call_ ## _func_<N+1, SIZE, THandlers>()(handlers, object); \
|
||||
} \
|
||||
}; \
|
||||
template <int SIZE, typename THandlers> \
|
||||
struct call_ ## _func_<SIZE, SIZE, THandlers> { \
|
||||
void operator()(THandlers&, osmium::_type_&) {} \
|
||||
};
|
||||
|
||||
namespace osmium {
|
||||
|
||||
class Node;
|
||||
class Way;
|
||||
class Relation;
|
||||
class Area;
|
||||
class Changeset;
|
||||
|
||||
namespace handler {
|
||||
|
||||
/**
|
||||
* This handler allows chaining of any number of handlers into a single
|
||||
* handler.
|
||||
*/
|
||||
template <typename... THandler>
|
||||
class ChainHandler : public osmium::handler::Handler {
|
||||
|
||||
using handlers_type = std::tuple<THandler&...>;
|
||||
handlers_type m_handlers;
|
||||
|
||||
template <int N, int SIZE, typename THandlers>
|
||||
struct call_flush {
|
||||
void operator()(THandlers& handlers) {
|
||||
std::get<N>(handlers).flush();
|
||||
call_flush<N + 1, SIZE, THandlers>()(handlers);
|
||||
}
|
||||
}; // struct call_flush
|
||||
|
||||
template <int SIZE, typename THandlers>
|
||||
struct call_flush<SIZE, SIZE, THandlers> {
|
||||
void operator()(THandlers&) {}
|
||||
}; // struct call_flush
|
||||
|
||||
OSMIUM_CHAIN_HANDLER_CALL(node, Node)
|
||||
OSMIUM_CHAIN_HANDLER_CALL(way, Way)
|
||||
OSMIUM_CHAIN_HANDLER_CALL(relation, Relation)
|
||||
OSMIUM_CHAIN_HANDLER_CALL(changeset, Changeset)
|
||||
OSMIUM_CHAIN_HANDLER_CALL(area, Area)
|
||||
|
||||
public:
|
||||
|
||||
explicit ChainHandler(THandler&... handlers) :
|
||||
m_handlers(handlers...) {
|
||||
}
|
||||
|
||||
void node(osmium::Node& node) {
|
||||
call_node<0, sizeof...(THandler), handlers_type>()(m_handlers, node);
|
||||
}
|
||||
|
||||
void way(osmium::Way& way) {
|
||||
call_way<0, sizeof...(THandler), handlers_type>()(m_handlers, way);
|
||||
}
|
||||
|
||||
void relation(osmium::Relation& relation) {
|
||||
call_relation<0, sizeof...(THandler), handlers_type>()(m_handlers, relation);
|
||||
}
|
||||
|
||||
void changeset(osmium::Changeset& changeset) {
|
||||
call_changeset<0, sizeof...(THandler), handlers_type>()(m_handlers, changeset);
|
||||
}
|
||||
|
||||
void area(osmium::Area& area) {
|
||||
call_area<0, sizeof...(THandler), handlers_type>()(m_handlers, area);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
call_flush<0, sizeof...(THandler), handlers_type>()(m_handlers);
|
||||
}
|
||||
|
||||
}; // class ChainHandler
|
||||
|
||||
} // namespace handler
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_HANDLER_CHAIN_HPP
|
||||
@@ -0,0 +1,154 @@
|
||||
#ifndef OSMIUM_HANDLER_CHECK_ORDER_HPP
|
||||
#define OSMIUM_HANDLER_CHECK_ORDER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <osmium/handler.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/object_comparisons.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* Exception thrown when a method in the CheckOrder class detects
|
||||
* that the input is out of order.
|
||||
*/
|
||||
struct out_of_order_error : public std::runtime_error {
|
||||
|
||||
osmium::object_id_type object_id;
|
||||
|
||||
explicit out_of_order_error(const std::string& what, osmium::object_id_type id) :
|
||||
std::runtime_error(what),
|
||||
object_id(id) {
|
||||
}
|
||||
|
||||
explicit out_of_order_error(const char* what, osmium::object_id_type id) :
|
||||
std::runtime_error(what),
|
||||
object_id(id) {
|
||||
}
|
||||
|
||||
}; // struct out_of_order_error
|
||||
|
||||
namespace handler {
|
||||
|
||||
/**
|
||||
* Handler that can be used to check that an OSM file is ordered
|
||||
* correctly. Ordered in this case refers to the usual order in OSM
|
||||
* files: First nodes in the order of their IDs, then ways in the order
|
||||
* of their IDs, then relations in the order or their IDs. Negative
|
||||
* IDs are ordered first then positive IDs, both ordered by absolute
|
||||
* value.
|
||||
*
|
||||
* IDs have to be unique for each type. This check will fail for
|
||||
* history files.
|
||||
*
|
||||
* To use this, add a CheckOrder member variable to your handler and
|
||||
* call the node(), way(), and relation() methods from your node(),
|
||||
* way(), and relations() handlers, respectively. An out_of_order_error
|
||||
* exception will be thrown when the input is not in order.
|
||||
*/
|
||||
class CheckOrder : public osmium::handler::Handler {
|
||||
|
||||
osmium::object_id_type m_max_node_id = std::numeric_limits<osmium::object_id_type>::min();
|
||||
osmium::object_id_type m_max_way_id = std::numeric_limits<osmium::object_id_type>::min();
|
||||
osmium::object_id_type m_max_relation_id = std::numeric_limits<osmium::object_id_type>::min();
|
||||
|
||||
public:
|
||||
|
||||
void node(const osmium::Node& node) {
|
||||
if (m_max_way_id > std::numeric_limits<osmium::object_id_type>::min()) {
|
||||
throw out_of_order_error{"Found a node after a way.", node.id()};
|
||||
}
|
||||
if (m_max_relation_id > std::numeric_limits<osmium::object_id_type>::min()) {
|
||||
throw out_of_order_error{"Found a node after a relation.", node.id()};
|
||||
}
|
||||
|
||||
if (m_max_node_id == node.id()) {
|
||||
throw out_of_order_error{"Node ID twice in input. Maybe you are using a history or change file?", node.id()};
|
||||
}
|
||||
if (id_order{}(node.id(), m_max_node_id)) {
|
||||
throw out_of_order_error{"Node IDs out of order.", node.id()};
|
||||
}
|
||||
m_max_node_id = node.id();
|
||||
}
|
||||
|
||||
void way(const osmium::Way& way) {
|
||||
if (m_max_relation_id > std::numeric_limits<osmium::object_id_type>::min()) {
|
||||
throw out_of_order_error{"Found a way after a relation.", way.id()};
|
||||
}
|
||||
|
||||
if (m_max_way_id == way.id()) {
|
||||
throw out_of_order_error{"Way ID twice in input. Maybe you are using a history or change file?", way.id()};
|
||||
}
|
||||
if (id_order{}(way.id(), m_max_way_id)) {
|
||||
throw out_of_order_error{"Way IDs out of order.", way.id()};
|
||||
}
|
||||
m_max_way_id = way.id();
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) {
|
||||
if (m_max_relation_id == relation.id()) {
|
||||
throw out_of_order_error{"Relation ID twice in input. Maybe you are using a history or change file?", relation.id()};
|
||||
}
|
||||
if (id_order{}(relation.id(), m_max_relation_id)) {
|
||||
throw out_of_order_error{"Relation IDs out of order.", relation.id()};
|
||||
}
|
||||
m_max_relation_id = relation.id();
|
||||
}
|
||||
|
||||
osmium::object_id_type max_node_id() const noexcept {
|
||||
return m_max_node_id;
|
||||
}
|
||||
|
||||
osmium::object_id_type max_way_id() const noexcept {
|
||||
return m_max_way_id;
|
||||
}
|
||||
|
||||
osmium::object_id_type max_relation_id() const noexcept {
|
||||
return m_max_relation_id;
|
||||
}
|
||||
|
||||
}; // class CheckOrder
|
||||
|
||||
} // namespace handler
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_HANDLER_CHECK_ORDER_HPP
|
||||
@@ -0,0 +1,112 @@
|
||||
#ifndef OSMIUM_HANDLER_DISK_STORE_HPP
|
||||
#define OSMIUM_HANDLER_DISK_STORE_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <osmium/handler.hpp>
|
||||
#include <osmium/index/map.hpp>
|
||||
#include <osmium/io/detail/read_write.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/memory/item_iterator.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/visitor.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace handler {
|
||||
|
||||
/**
|
||||
* Writes OSM data in the Osmium-internal serialized format to disk
|
||||
* keeping track of object offsets in the indexes given to the
|
||||
* constructor.
|
||||
*
|
||||
* Note: This handler will only work if either all object IDs are
|
||||
* positive or all object IDs are negative.
|
||||
*/
|
||||
class DiskStore : public osmium::handler::Handler {
|
||||
|
||||
using offset_index_type = osmium::index::map::Map<unsigned_object_id_type, std::size_t>;
|
||||
|
||||
std::size_t m_offset = 0;
|
||||
int m_data_fd;
|
||||
|
||||
offset_index_type& m_node_index;
|
||||
offset_index_type& m_way_index;
|
||||
offset_index_type& m_relation_index;
|
||||
|
||||
public:
|
||||
|
||||
explicit DiskStore(int data_fd, offset_index_type& node_index, offset_index_type& way_index, offset_index_type& relation_index) :
|
||||
m_data_fd(data_fd),
|
||||
m_node_index(node_index),
|
||||
m_way_index(way_index),
|
||||
m_relation_index(relation_index) {
|
||||
}
|
||||
|
||||
DiskStore(const DiskStore&) = delete;
|
||||
DiskStore& operator=(const DiskStore&) = delete;
|
||||
|
||||
~DiskStore() noexcept = default;
|
||||
|
||||
void node(const osmium::Node& node) {
|
||||
m_node_index.set(node.positive_id(), m_offset);
|
||||
m_offset += node.byte_size();
|
||||
}
|
||||
|
||||
void way(const osmium::Way& way) {
|
||||
m_way_index.set(way.positive_id(), m_offset);
|
||||
m_offset += way.byte_size();
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) {
|
||||
m_relation_index.set(relation.positive_id(), m_offset);
|
||||
m_offset += relation.byte_size();
|
||||
}
|
||||
|
||||
void operator()(const osmium::memory::Buffer& buffer) {
|
||||
osmium::io::detail::reliable_write(m_data_fd, buffer.data(), buffer.committed());
|
||||
osmium::apply(buffer.begin(), buffer.end(), *this);
|
||||
}
|
||||
|
||||
}; // class DiskStore
|
||||
|
||||
} // namespace handler
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_HANDLER_DISK_STORE_HPP
|
||||
@@ -0,0 +1,294 @@
|
||||
#ifndef OSMIUM_HANDLER_DUMP_HPP
|
||||
#define OSMIUM_HANDLER_DUMP_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <osmium/handler.hpp>
|
||||
#include <osmium/memory/collection.hpp>
|
||||
#include <osmium/memory/item.hpp>
|
||||
#include <osmium/osm/area.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/changeset.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/visitor.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace handler {
|
||||
|
||||
class Dump : public osmium::handler::Handler {
|
||||
|
||||
std::ostream* m_out;
|
||||
bool m_with_size;
|
||||
std::string m_prefix;
|
||||
|
||||
void print_title(const char* title, const osmium::memory::Item& item) {
|
||||
*m_out << m_prefix
|
||||
<< title
|
||||
<< ":";
|
||||
|
||||
if (m_with_size) {
|
||||
*m_out << " ["
|
||||
<< item.byte_size()
|
||||
<< "]";
|
||||
}
|
||||
|
||||
*m_out << "\n";
|
||||
}
|
||||
|
||||
void print_meta(const osmium::OSMObject& object) {
|
||||
*m_out << m_prefix
|
||||
<< " id="
|
||||
<< object.id()
|
||||
<< "\n";
|
||||
*m_out << m_prefix
|
||||
<< " version="
|
||||
<< object.version()
|
||||
<< "\n";
|
||||
*m_out << m_prefix
|
||||
<< " uid="
|
||||
<< object.uid()
|
||||
<< "\n";
|
||||
*m_out << m_prefix
|
||||
<< " user=|"
|
||||
<< object.user()
|
||||
<< "|\n";
|
||||
*m_out << m_prefix
|
||||
<< " changeset="
|
||||
<< object.changeset()
|
||||
<< "\n";
|
||||
*m_out << m_prefix
|
||||
<< " timestamp="
|
||||
<< object.timestamp().to_iso()
|
||||
<< "\n";
|
||||
*m_out << m_prefix
|
||||
<< " visible="
|
||||
<< (object.visible() ? "yes" : "no")
|
||||
<< "\n";
|
||||
|
||||
Dump dump{*m_out, m_with_size, m_prefix + " "};
|
||||
osmium::apply(object.cbegin(), object.cend(), dump);
|
||||
}
|
||||
|
||||
void print_location(const osmium::Node& node) {
|
||||
const osmium::Location& location = node.location();
|
||||
|
||||
if (location) {
|
||||
*m_out << m_prefix
|
||||
<< " lon="
|
||||
<< std::fixed
|
||||
<< std::setprecision(7)
|
||||
<< location.lon_without_check()
|
||||
<< "\n";
|
||||
*m_out << m_prefix
|
||||
<< " lat="
|
||||
<< location.lat_without_check()
|
||||
<< "\n";
|
||||
} else {
|
||||
*m_out << m_prefix
|
||||
<< " lon=\n"
|
||||
<< m_prefix
|
||||
<< " lat=\n";
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit Dump(std::ostream& out, bool with_size = true, const std::string& prefix = "") :
|
||||
m_out(&out),
|
||||
m_with_size(with_size),
|
||||
m_prefix(prefix) {
|
||||
}
|
||||
|
||||
void tag_list(const osmium::TagList& tags) {
|
||||
print_title("TAGS", tags);
|
||||
for (const auto& tag : tags) {
|
||||
*m_out << m_prefix
|
||||
<< " k=|"
|
||||
<< tag.key()
|
||||
<< "| v=|"
|
||||
<< tag.value()
|
||||
<< "|"
|
||||
<< "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void way_node_list(const osmium::WayNodeList& wnl) {
|
||||
print_title("NODES", wnl);
|
||||
for (const auto& node_ref : wnl) {
|
||||
*m_out << m_prefix
|
||||
<< " ref="
|
||||
<< node_ref.ref();
|
||||
if (node_ref.location()) {
|
||||
*m_out << " pos="
|
||||
<< node_ref.location();
|
||||
}
|
||||
*m_out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void relation_member_list(const osmium::RelationMemberList& rml) {
|
||||
print_title("MEMBERS", rml);
|
||||
for (const auto& member : rml) {
|
||||
*m_out << m_prefix
|
||||
<< " type="
|
||||
<< item_type_to_name(member.type())
|
||||
<< " ref="
|
||||
<< member.ref()
|
||||
<< " role=|"
|
||||
<< member.role()
|
||||
<< "|\n";
|
||||
if (member.full_member()) {
|
||||
Dump dump(*m_out, m_with_size, m_prefix + " | ");
|
||||
osmium::apply_item(member.get_object(), dump);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void outer_ring(const osmium::OuterRing& ring) {
|
||||
print_title("OUTER RING", ring);
|
||||
for (const auto& node_ref : ring) {
|
||||
*m_out << m_prefix
|
||||
<< " ref="
|
||||
<< node_ref.ref();
|
||||
if (node_ref.location()) {
|
||||
*m_out << " pos="
|
||||
<< node_ref.location();
|
||||
}
|
||||
*m_out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void inner_ring(const osmium::InnerRing& ring) {
|
||||
print_title("INNER RING", ring);
|
||||
for (const auto& node_ref : ring) {
|
||||
*m_out << m_prefix
|
||||
<< " ref="
|
||||
<< node_ref.ref();
|
||||
if (node_ref.location()) {
|
||||
*m_out << " pos="
|
||||
<< node_ref.location();
|
||||
}
|
||||
*m_out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void node(const osmium::Node& node) {
|
||||
print_title("NODE", node);
|
||||
print_meta(node);
|
||||
print_location(node);
|
||||
}
|
||||
|
||||
void way(const osmium::Way& way) {
|
||||
print_title("WAY", way);
|
||||
print_meta(way);
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) {
|
||||
print_title("RELATION", relation);
|
||||
print_meta(relation);
|
||||
}
|
||||
|
||||
void area(const osmium::Area& area) {
|
||||
print_title("AREA", area);
|
||||
print_meta(area);
|
||||
}
|
||||
|
||||
void changeset(const osmium::Changeset& changeset) {
|
||||
print_title("CHANGESET", changeset);
|
||||
*m_out << m_prefix
|
||||
<< " id="
|
||||
<< changeset.id()
|
||||
<< "\n";
|
||||
*m_out << m_prefix
|
||||
<< " num_changes="
|
||||
<< changeset.num_changes()
|
||||
<< "\n";
|
||||
*m_out << m_prefix
|
||||
<< " uid="
|
||||
<< changeset.uid()
|
||||
<< "\n";
|
||||
*m_out << m_prefix
|
||||
<< " user=|"
|
||||
<< changeset.user()
|
||||
<< "|\n";
|
||||
*m_out << m_prefix
|
||||
<< " created_at="
|
||||
<< changeset.created_at().to_iso()
|
||||
<< "\n";
|
||||
*m_out << m_prefix
|
||||
<< " closed_at="
|
||||
<< changeset.closed_at().to_iso()
|
||||
<< "\n";
|
||||
*m_out << m_prefix
|
||||
<< " bounds=";
|
||||
|
||||
if (changeset.bounds()) {
|
||||
*m_out << '('
|
||||
<< changeset.bounds().bottom_left().lon_without_check()
|
||||
<< ','
|
||||
<< changeset.bounds().bottom_left().lat_without_check()
|
||||
<< ','
|
||||
<< changeset.bounds().top_right().lon_without_check()
|
||||
<< ','
|
||||
<< changeset.bounds().top_right().lat_without_check()
|
||||
<< ')';
|
||||
} else {
|
||||
*m_out << "(undefined)";
|
||||
}
|
||||
|
||||
*m_out << "\n";
|
||||
|
||||
Dump dump{*m_out, m_with_size, m_prefix + " "};
|
||||
osmium::apply(changeset.cbegin(), changeset.cend(), dump);
|
||||
}
|
||||
|
||||
}; // class Dump
|
||||
|
||||
} // namespace handler
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_HANDLER_DUMP_HPP
|
||||
@@ -0,0 +1,186 @@
|
||||
#ifndef OSMIUM_HANDLER_NODE_LOCATIONS_FOR_WAYS_HPP
|
||||
#define OSMIUM_HANDLER_NODE_LOCATIONS_FOR_WAYS_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include <osmium/handler.hpp>
|
||||
#include <osmium/index/index.hpp>
|
||||
#include <osmium/index/map/dummy.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
#include <osmium/index/node_locations_map.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace handler {
|
||||
|
||||
using dummy_type = osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location>;
|
||||
|
||||
/**
|
||||
* Handler to retrieve locations from nodes and add them to ways.
|
||||
*
|
||||
* @tparam TStoragePosIDs Class that handles the actual storage of the node locations
|
||||
* (for positive IDs). It must support the set(id, value) and
|
||||
* get(id) methods.
|
||||
* @tparam TStorageNegIDs Same but for negative IDs.
|
||||
*/
|
||||
template <typename TStoragePosIDs, typename TStorageNegIDs = dummy_type>
|
||||
class NodeLocationsForWays : public osmium::handler::Handler {
|
||||
|
||||
template <typename T>
|
||||
using based_on_map = std::is_base_of<osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>, T>;
|
||||
|
||||
static_assert(based_on_map<TStoragePosIDs>::value, "Index class must be derived from osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>");
|
||||
static_assert(based_on_map<TStorageNegIDs>::value, "Index class must be derived from osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>");
|
||||
|
||||
public:
|
||||
|
||||
using index_pos_type = TStoragePosIDs;
|
||||
using index_neg_type = TStorageNegIDs;
|
||||
|
||||
private:
|
||||
|
||||
/// Object that handles the actual storage of the node locations (with positive IDs).
|
||||
TStoragePosIDs& m_storage_pos;
|
||||
|
||||
/// Object that handles the actual storage of the node locations (with negative IDs).
|
||||
TStorageNegIDs& m_storage_neg;
|
||||
|
||||
osmium::unsigned_object_id_type m_last_id = 0;
|
||||
|
||||
bool m_ignore_errors = false;
|
||||
|
||||
bool m_must_sort = false;
|
||||
|
||||
// It is okay to have this static dummy instance, even when using several threads,
|
||||
// because it is read-only.
|
||||
static dummy_type& get_dummy() {
|
||||
static dummy_type instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit NodeLocationsForWays(TStoragePosIDs& storage_pos,
|
||||
TStorageNegIDs& storage_neg = get_dummy()) :
|
||||
m_storage_pos(storage_pos),
|
||||
m_storage_neg(storage_neg) {
|
||||
}
|
||||
|
||||
NodeLocationsForWays(const NodeLocationsForWays&) = delete;
|
||||
NodeLocationsForWays& operator=(const NodeLocationsForWays&) = delete;
|
||||
|
||||
NodeLocationsForWays(NodeLocationsForWays&&) = default;
|
||||
NodeLocationsForWays& operator=(NodeLocationsForWays&&) = default;
|
||||
|
||||
~NodeLocationsForWays() noexcept = default;
|
||||
|
||||
void ignore_errors() {
|
||||
m_ignore_errors = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the location of the node in the storage.
|
||||
*/
|
||||
void node(const osmium::Node& node) {
|
||||
if (node.positive_id() < m_last_id) {
|
||||
m_must_sort = true;
|
||||
}
|
||||
m_last_id = node.positive_id();
|
||||
|
||||
const auto id = node.id();
|
||||
if (id >= 0) {
|
||||
m_storage_pos.set(static_cast<osmium::unsigned_object_id_type>( id), node.location());
|
||||
} else {
|
||||
m_storage_neg.set(static_cast<osmium::unsigned_object_id_type>(-id), node.location());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get location of node with given id.
|
||||
*/
|
||||
osmium::Location get_node_location(const osmium::object_id_type id) const {
|
||||
if (id >= 0) {
|
||||
return m_storage_pos.get_noexcept(static_cast<osmium::unsigned_object_id_type>( id));
|
||||
} else {
|
||||
return m_storage_neg.get_noexcept(static_cast<osmium::unsigned_object_id_type>(-id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve locations of all nodes in the way from storage and add
|
||||
* them to the way object.
|
||||
*/
|
||||
void way(osmium::Way& way) {
|
||||
if (m_must_sort) {
|
||||
m_storage_pos.sort();
|
||||
m_storage_neg.sort();
|
||||
m_must_sort = false;
|
||||
m_last_id = std::numeric_limits<osmium::unsigned_object_id_type>::max();
|
||||
}
|
||||
bool error = false;
|
||||
for (auto& node_ref : way.nodes()) {
|
||||
node_ref.set_location(get_node_location(node_ref.ref()));
|
||||
if (!node_ref.location()) {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
if (!m_ignore_errors && error) {
|
||||
throw osmium::not_found{"location for one or more nodes not found in node location index"};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call clear on the location indexes. Makes the
|
||||
* NodeLocationsForWays handler unusable. Used to explicitly free
|
||||
* memory if thats needed.
|
||||
*/
|
||||
void clear() {
|
||||
m_storage_pos.clear();
|
||||
m_storage_neg.clear();
|
||||
}
|
||||
|
||||
}; // class NodeLocationsForWays
|
||||
|
||||
} // namespace handler
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_HANDLER_NODE_LOCATIONS_FOR_WAYS_HPP
|
||||
@@ -0,0 +1,108 @@
|
||||
#ifndef OSMIUM_HANDLER_OBJECT_RELATIONS_HPP
|
||||
#define OSMIUM_HANDLER_OBJECT_RELATIONS_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/handler.hpp>
|
||||
#include <osmium/index/multimap.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace handler {
|
||||
|
||||
/**
|
||||
* This handler updates the indexes given to the constructor with
|
||||
* the relations between objects.
|
||||
*
|
||||
* Note: This handler will only work if either all object IDs are
|
||||
* positive or all object IDs are negative.
|
||||
*/
|
||||
class ObjectRelations : public osmium::handler::Handler {
|
||||
|
||||
using index_type = osmium::index::multimap::Multimap<unsigned_object_id_type, unsigned_object_id_type>;
|
||||
|
||||
index_type& m_index_n2w;
|
||||
index_type& m_index_n2r;
|
||||
index_type& m_index_w2r;
|
||||
index_type& m_index_r2r;
|
||||
|
||||
public:
|
||||
|
||||
explicit ObjectRelations(index_type& n2w, index_type& n2r, index_type& w2r, index_type& r2r) :
|
||||
m_index_n2w(n2w),
|
||||
m_index_n2r(n2r),
|
||||
m_index_w2r(w2r),
|
||||
m_index_r2r(r2r) {
|
||||
}
|
||||
|
||||
ObjectRelations(const ObjectRelations&) = delete;
|
||||
ObjectRelations& operator=(const ObjectRelations&) = delete;
|
||||
|
||||
~ObjectRelations() noexcept = default;
|
||||
|
||||
void way(const osmium::Way& way) {
|
||||
for (const auto& node_ref : way.nodes()) {
|
||||
m_index_n2w.set(node_ref.positive_ref(), way.positive_id());
|
||||
}
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) {
|
||||
for (const auto& member : relation.members()) {
|
||||
switch (member.type()) {
|
||||
case osmium::item_type::node:
|
||||
m_index_n2r.set(member.positive_ref(), relation.positive_id());
|
||||
break;
|
||||
case osmium::item_type::way:
|
||||
m_index_w2r.set(member.positive_ref(), relation.positive_id());
|
||||
break;
|
||||
case osmium::item_type::relation:
|
||||
m_index_r2r.set(member.positive_ref(), relation.positive_id());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // class ObjectRelations
|
||||
|
||||
} // namespace handler
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_HANDLER_OBJECT_RELATIONS_HPP
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef OSMIUM_INDEX_BOOL_VECTOR_HPP
|
||||
#define OSMIUM_INDEX_BOOL_VECTOR_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/index/id_set.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
/// @deprecated Use osmium::index::IdSet instead.
|
||||
template <typename T>
|
||||
using BoolVector = IdSet<T>;
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_BOOL_VECTOR_HPP
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef OSMIUM_INDEX_DETAIL_CREATE_MAP_WITH_FD_HPP
|
||||
#define OSMIUM_INDEX_DETAIL_CREATE_MAP_WITH_FD_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
inline T* create_map_with_fd(const std::vector<std::string>& config) {
|
||||
if (config.size() == 1) {
|
||||
return new T{};
|
||||
}
|
||||
assert(config.size() > 1);
|
||||
const std::string& filename = config[1];
|
||||
const int fd = ::open(filename.c_str(), O_CREAT | O_RDWR, 0644);
|
||||
if (fd == -1) {
|
||||
throw std::runtime_error{std::string{"can't open file '"} + filename + "': " + std::strerror(errno)};
|
||||
}
|
||||
return new T{fd};
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_DETAIL_CREATE_MAP_WITH_FD_HPP
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef OSMIUM_INDEX_DETAIL_MMAP_VECTOR_ANON_HPP
|
||||
#define OSMIUM_INDEX_DETAIL_MMAP_VECTOR_ANON_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <osmium/index/detail/mmap_vector_base.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* This class looks and behaves like STL vector, but uses mmap
|
||||
* internally.
|
||||
*/
|
||||
template <typename T>
|
||||
class mmap_vector_anon : public mmap_vector_base<T> {
|
||||
|
||||
public:
|
||||
|
||||
mmap_vector_anon() :
|
||||
mmap_vector_base<T>() {
|
||||
}
|
||||
|
||||
~mmap_vector_anon() noexcept = default;
|
||||
|
||||
}; // class mmap_vector_anon
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
#endif // OSMIUM_INDEX_DETAIL_MMAP_VECTOR_ANON_HPP
|
||||
@@ -0,0 +1,191 @@
|
||||
#ifndef OSMIUM_INDEX_DETAIL_MMAP_VECTOR_BASE_HPP
|
||||
#define OSMIUM_INDEX_DETAIL_MMAP_VECTOR_BASE_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <new> // IWYU pragma: keep
|
||||
#include <stdexcept>
|
||||
|
||||
#include <osmium/index/index.hpp>
|
||||
#include <osmium/util/memory_mapping.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr size_t mmap_vector_size_increment = 1024 * 1024;
|
||||
|
||||
/**
|
||||
* This is a base class for implementing classes that look like
|
||||
* STL vector but use mmap internally. Do not use this class itself,
|
||||
* use the derived classes mmap_vector_anon or mmap_vector_file.
|
||||
*/
|
||||
template <typename T>
|
||||
class mmap_vector_base {
|
||||
|
||||
protected:
|
||||
|
||||
size_t m_size;
|
||||
osmium::util::TypedMemoryMapping<T> m_mapping;
|
||||
|
||||
public:
|
||||
|
||||
mmap_vector_base(int fd, size_t capacity, size_t size = 0) :
|
||||
m_size(size),
|
||||
m_mapping(capacity, osmium::util::MemoryMapping::mapping_mode::write_shared, fd) {
|
||||
assert(size <= capacity);
|
||||
std::fill(data() + size, data() + capacity, osmium::index::empty_value<T>());
|
||||
shrink_to_fit();
|
||||
}
|
||||
|
||||
explicit mmap_vector_base(size_t capacity = mmap_vector_size_increment) :
|
||||
m_size(0),
|
||||
m_mapping(capacity) {
|
||||
std::fill_n(data(), capacity, osmium::index::empty_value<T>());
|
||||
}
|
||||
|
||||
~mmap_vector_base() noexcept = default;
|
||||
|
||||
using value_type = T;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using iterator = value_type*;
|
||||
using const_iterator = const value_type*;
|
||||
|
||||
void close() {
|
||||
m_mapping.unmap();
|
||||
}
|
||||
|
||||
size_t capacity() const noexcept {
|
||||
return m_mapping.size();
|
||||
}
|
||||
|
||||
size_t size() const noexcept {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
bool empty() const noexcept {
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
const_pointer data() const {
|
||||
return m_mapping.begin();
|
||||
}
|
||||
|
||||
pointer data() {
|
||||
return m_mapping.begin();
|
||||
}
|
||||
|
||||
const_reference operator[](size_t n) const {
|
||||
assert(n < m_size);
|
||||
return data()[n];
|
||||
}
|
||||
|
||||
reference operator[](size_t n) {
|
||||
assert(n < m_size);
|
||||
return data()[n];
|
||||
}
|
||||
|
||||
value_type at(size_t n) const {
|
||||
if (n >= m_size) {
|
||||
throw std::out_of_range{"out of range"};
|
||||
}
|
||||
return data()[n];
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
void shrink_to_fit() {
|
||||
while (m_size > 0 && data()[m_size - 1] == osmium::index::empty_value<value_type>()) {
|
||||
--m_size;
|
||||
}
|
||||
}
|
||||
|
||||
void push_back(const_reference value) {
|
||||
resize(m_size + 1);
|
||||
data()[m_size - 1] = value;
|
||||
}
|
||||
|
||||
void reserve(size_t new_capacity) {
|
||||
if (new_capacity > capacity()) {
|
||||
const size_t old_capacity = capacity();
|
||||
m_mapping.resize(new_capacity);
|
||||
std::fill(data() + old_capacity, data() + new_capacity, osmium::index::empty_value<value_type>());
|
||||
}
|
||||
}
|
||||
|
||||
void resize(size_t new_size) {
|
||||
if (new_size > capacity()) {
|
||||
reserve(new_size + osmium::detail::mmap_vector_size_increment);
|
||||
}
|
||||
m_size = new_size;
|
||||
}
|
||||
|
||||
iterator begin() noexcept {
|
||||
return data();
|
||||
}
|
||||
|
||||
iterator end() noexcept {
|
||||
return data() + m_size;
|
||||
}
|
||||
|
||||
const_iterator begin() const noexcept {
|
||||
return data();
|
||||
}
|
||||
|
||||
const_iterator end() const noexcept {
|
||||
return data() + m_size;
|
||||
}
|
||||
|
||||
const_iterator cbegin() const noexcept {
|
||||
return data();
|
||||
}
|
||||
|
||||
const_iterator cend() const noexcept {
|
||||
return data() + m_size;
|
||||
}
|
||||
|
||||
}; // class mmap_vector_base
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_DETAIL_MMAP_VECTOR_BASE_HPP
|
||||
@@ -0,0 +1,89 @@
|
||||
#ifndef OSMIUM_INDEX_DETAIL_MMAP_VECTOR_FILE_HPP
|
||||
#define OSMIUM_INDEX_DETAIL_MMAP_VECTOR_FILE_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <osmium/index/detail/mmap_vector_base.hpp>
|
||||
#include <osmium/index/detail/tmpfile.hpp>
|
||||
#include <osmium/util/file.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* This class looks and behaves like STL vector, but mmap's a file
|
||||
* internally.
|
||||
*/
|
||||
template <typename T>
|
||||
class mmap_vector_file : public mmap_vector_base<T> {
|
||||
|
||||
static std::size_t filesize(int fd) {
|
||||
const auto size = osmium::util::file_size(fd);
|
||||
|
||||
if (size % sizeof(T) != 0) {
|
||||
throw std::runtime_error{"Index file has wrong size (must be multiple of " + std::to_string(sizeof(T)) + ")."};
|
||||
}
|
||||
|
||||
return size / sizeof(T);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
mmap_vector_file() :
|
||||
mmap_vector_base<T>(
|
||||
osmium::detail::create_tmp_file(),
|
||||
osmium::detail::mmap_vector_size_increment) {
|
||||
}
|
||||
|
||||
explicit mmap_vector_file(int fd) :
|
||||
mmap_vector_base<T>(
|
||||
fd,
|
||||
std::max(osmium::detail::mmap_vector_size_increment, filesize(fd)),
|
||||
filesize(fd)) {
|
||||
}
|
||||
|
||||
~mmap_vector_file() noexcept = default;
|
||||
|
||||
}; // class mmap_vector_file
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_DETAIL_MMAP_VECTOR_FILE_HPP
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef OSMIUM_INDEX_DETAIL_TMPFILE_HPP
|
||||
#define OSMIUM_INDEX_DETAIL_TMPFILE_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <system_error>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Create and open a temporary file. It is removed after opening.
|
||||
*
|
||||
* @returns File descriptor of temporary file.
|
||||
* @throws std::system_error if something went wrong.
|
||||
*/
|
||||
inline int create_tmp_file() {
|
||||
FILE* file = ::tmpfile();
|
||||
if (!file) {
|
||||
throw std::system_error{errno, std::system_category(), "tempfile failed"};
|
||||
}
|
||||
return fileno(file);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_DETAIL_TMPFILE_HPP
|
||||
@@ -0,0 +1,264 @@
|
||||
#ifndef OSMIUM_INDEX_DETAIL_VECTOR_MAP_HPP
|
||||
#define OSMIUM_INDEX_DETAIL_VECTOR_MAP_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/index/index.hpp>
|
||||
#include <osmium/index/map.hpp>
|
||||
#include <osmium/io/detail/read_write.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace map {
|
||||
|
||||
template <typename TVector, typename TId, typename TValue>
|
||||
class VectorBasedDenseMap : public Map<TId, TValue> {
|
||||
|
||||
TVector m_vector;
|
||||
|
||||
public:
|
||||
|
||||
using element_type = TValue;
|
||||
using vector_type = TVector;
|
||||
using iterator = typename vector_type::iterator;
|
||||
using const_iterator = typename vector_type::const_iterator;
|
||||
|
||||
VectorBasedDenseMap() :
|
||||
m_vector() {
|
||||
}
|
||||
|
||||
explicit VectorBasedDenseMap(int fd) :
|
||||
m_vector(fd) {
|
||||
}
|
||||
|
||||
~VectorBasedDenseMap() noexcept final = default;
|
||||
|
||||
void reserve(const std::size_t size) final {
|
||||
m_vector.reserve(size);
|
||||
}
|
||||
|
||||
void set(const TId id, const TValue value) final {
|
||||
if (size() <= id) {
|
||||
m_vector.resize(id+1);
|
||||
}
|
||||
m_vector[id] = value;
|
||||
}
|
||||
|
||||
TValue get(const TId id) const final {
|
||||
if (id >= m_vector.size()) {
|
||||
throw osmium::not_found{id};
|
||||
}
|
||||
const TValue value = m_vector[id];
|
||||
if (value == osmium::index::empty_value<TValue>()) {
|
||||
throw osmium::not_found{id};
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
TValue get_noexcept(const TId id) const noexcept final {
|
||||
if (id >= m_vector.size()) {
|
||||
return osmium::index::empty_value<TValue>();
|
||||
}
|
||||
return m_vector[id];
|
||||
}
|
||||
|
||||
std::size_t size() const final {
|
||||
return m_vector.size();
|
||||
}
|
||||
|
||||
std::size_t byte_size() const {
|
||||
return m_vector.size() * sizeof(element_type);
|
||||
}
|
||||
|
||||
std::size_t used_memory() const final {
|
||||
return sizeof(TValue) * size();
|
||||
}
|
||||
|
||||
void clear() final {
|
||||
m_vector.clear();
|
||||
m_vector.shrink_to_fit();
|
||||
}
|
||||
|
||||
void dump_as_array(const int fd) final {
|
||||
osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(m_vector.data()), byte_size());
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
return m_vector.begin();
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return m_vector.end();
|
||||
}
|
||||
|
||||
const_iterator cbegin() const {
|
||||
return m_vector.cbegin();
|
||||
}
|
||||
|
||||
const_iterator cend() const {
|
||||
return m_vector.cend();
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
return m_vector.cbegin();
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return m_vector.cend();
|
||||
}
|
||||
|
||||
}; // class VectorBasedDenseMap
|
||||
|
||||
|
||||
template <typename TId, typename TValue, template<typename...> class TVector>
|
||||
class VectorBasedSparseMap : public Map<TId, TValue> {
|
||||
|
||||
public:
|
||||
|
||||
using element_type = typename std::pair<TId, TValue>;
|
||||
using vector_type = TVector<element_type>;
|
||||
using iterator = typename vector_type::iterator;
|
||||
using const_iterator = typename vector_type::const_iterator;
|
||||
|
||||
private:
|
||||
|
||||
vector_type m_vector;
|
||||
|
||||
typename vector_type::const_iterator find_id(const TId id) const noexcept {
|
||||
const element_type element {
|
||||
id,
|
||||
osmium::index::empty_value<TValue>()
|
||||
};
|
||||
return std::lower_bound(m_vector.begin(), m_vector.end(), element, [](const element_type& a, const element_type& b) {
|
||||
return a.first < b.first;
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
VectorBasedSparseMap() :
|
||||
m_vector() {
|
||||
}
|
||||
|
||||
explicit VectorBasedSparseMap(int fd) :
|
||||
m_vector(fd) {
|
||||
}
|
||||
|
||||
~VectorBasedSparseMap() final = default;
|
||||
|
||||
void set(const TId id, const TValue value) final {
|
||||
m_vector.push_back(element_type(id, value));
|
||||
}
|
||||
|
||||
TValue get(const TId id) const final {
|
||||
const auto result = find_id(id);
|
||||
if (result == m_vector.end() || result->first != id) {
|
||||
throw osmium::not_found{id};
|
||||
}
|
||||
|
||||
return result->second;
|
||||
}
|
||||
|
||||
TValue get_noexcept(const TId id) const noexcept final {
|
||||
const auto result = find_id(id);
|
||||
if (result == m_vector.end() || result->first != id) {
|
||||
return osmium::index::empty_value<TValue>();
|
||||
}
|
||||
|
||||
return result->second;
|
||||
}
|
||||
|
||||
std::size_t size() const final {
|
||||
return m_vector.size();
|
||||
}
|
||||
|
||||
std::size_t byte_size() const {
|
||||
return m_vector.size() * sizeof(element_type);
|
||||
}
|
||||
|
||||
std::size_t used_memory() const final {
|
||||
return sizeof(element_type) * size();
|
||||
}
|
||||
|
||||
void clear() final {
|
||||
m_vector.clear();
|
||||
m_vector.shrink_to_fit();
|
||||
}
|
||||
|
||||
void sort() final {
|
||||
std::sort(m_vector.begin(), m_vector.end());
|
||||
}
|
||||
|
||||
void dump_as_list(const int fd) final {
|
||||
osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(m_vector.data()), byte_size());
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
return m_vector.begin();
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return m_vector.end();
|
||||
}
|
||||
|
||||
const_iterator cbegin() const {
|
||||
return m_vector.cbegin();
|
||||
}
|
||||
|
||||
const_iterator cend() const {
|
||||
return m_vector.cend();
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
return m_vector.cbegin();
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return m_vector.cend();
|
||||
}
|
||||
|
||||
}; // class VectorBasedSparseMap
|
||||
|
||||
} // namespace map
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_DETAIL_VECTOR_MAP_HPP
|
||||
@@ -0,0 +1,186 @@
|
||||
#ifndef OSMIUM_INDEX_DETAIL_VECTOR_MULTIMAP_HPP
|
||||
#define OSMIUM_INDEX_DETAIL_VECTOR_MULTIMAP_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/index/index.hpp>
|
||||
#include <osmium/index/multimap.hpp>
|
||||
#include <osmium/io/detail/read_write.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace multimap {
|
||||
|
||||
template <typename TId, typename TValue, template<typename...> class TVector>
|
||||
class VectorBasedSparseMultimap : public Multimap<TId, TValue> {
|
||||
|
||||
public:
|
||||
|
||||
using element_type = typename std::pair<TId, TValue>;
|
||||
using vector_type = TVector<element_type>;
|
||||
using iterator = typename vector_type::iterator;
|
||||
using const_iterator = typename vector_type::const_iterator;
|
||||
|
||||
private:
|
||||
|
||||
vector_type m_vector;
|
||||
|
||||
static bool is_removed(element_type& element) {
|
||||
return element.second == osmium::index::empty_value<TValue>();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
VectorBasedSparseMultimap() :
|
||||
m_vector() {
|
||||
}
|
||||
|
||||
explicit VectorBasedSparseMultimap(int fd) :
|
||||
m_vector(fd) {
|
||||
}
|
||||
|
||||
~VectorBasedSparseMultimap() noexcept final = default;
|
||||
|
||||
void set(const TId id, const TValue value) final {
|
||||
m_vector.push_back(element_type(id, value));
|
||||
}
|
||||
|
||||
void unsorted_set(const TId id, const TValue value) {
|
||||
m_vector.push_back(element_type(id, value));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> get_all(const TId id) {
|
||||
const element_type element {
|
||||
id,
|
||||
osmium::index::empty_value<TValue>()
|
||||
};
|
||||
return std::equal_range(m_vector.begin(), m_vector.end(), element, [](const element_type& a, const element_type& b) {
|
||||
return a.first < b.first;
|
||||
});
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> get_all(const TId id) const {
|
||||
const element_type element {
|
||||
id,
|
||||
osmium::index::empty_value<TValue>()
|
||||
};
|
||||
return std::equal_range(m_vector.cbegin(), m_vector.cend(), element, [](const element_type& a, const element_type& b) {
|
||||
return a.first < b.first;
|
||||
});
|
||||
}
|
||||
|
||||
size_t size() const final {
|
||||
return m_vector.size();
|
||||
}
|
||||
|
||||
size_t byte_size() const {
|
||||
return m_vector.size() * sizeof(element_type);
|
||||
}
|
||||
|
||||
size_t used_memory() const final {
|
||||
return sizeof(element_type) * size();
|
||||
}
|
||||
|
||||
void clear() final {
|
||||
m_vector.clear();
|
||||
m_vector.shrink_to_fit();
|
||||
}
|
||||
|
||||
void sort() final {
|
||||
std::sort(m_vector.begin(), m_vector.end());
|
||||
}
|
||||
|
||||
void remove(const TId id, const TValue value) {
|
||||
const auto r = get_all(id);
|
||||
for (auto it = r.first; it != r.second; ++it) {
|
||||
if (it->second == value) {
|
||||
it->second = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void consolidate() {
|
||||
std::sort(m_vector.begin(), m_vector.end());
|
||||
}
|
||||
|
||||
void erase_removed() {
|
||||
m_vector.erase(
|
||||
std::remove_if(m_vector.begin(), m_vector.end(), is_removed),
|
||||
m_vector.end()
|
||||
);
|
||||
}
|
||||
|
||||
void dump_as_list(const int fd) final {
|
||||
osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(m_vector.data()), byte_size());
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
return m_vector.begin();
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return m_vector.end();
|
||||
}
|
||||
|
||||
const_iterator cbegin() const {
|
||||
return m_vector.cbegin();
|
||||
}
|
||||
|
||||
const_iterator cend() const {
|
||||
return m_vector.cend();
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
return m_vector.cbegin();
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return m_vector.cend();
|
||||
}
|
||||
|
||||
}; // class VectorBasedSparseMultimap
|
||||
|
||||
} // namespace multimap
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_DETAIL_VECTOR_MULTIMAP_HPP
|
||||
@@ -0,0 +1,448 @@
|
||||
#ifndef OSMIUM_INDEX_ID_SET_HPP
|
||||
#define OSMIUM_INDEX_ID_SET_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
/**
|
||||
* Virtual parent class for IdSets. Use one of the implementations
|
||||
* provided.
|
||||
*/
|
||||
template <typename T>
|
||||
class IdSet {
|
||||
|
||||
public:
|
||||
|
||||
virtual ~IdSet() = default;
|
||||
|
||||
/**
|
||||
* Add the given Id to the set.
|
||||
*/
|
||||
virtual void set(T id) = 0;
|
||||
|
||||
/**
|
||||
* Is the Id in the set?
|
||||
*/
|
||||
virtual bool get(T id) const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Is the set empty?
|
||||
*/
|
||||
virtual bool empty() const = 0;
|
||||
|
||||
/**
|
||||
* Clear the set.
|
||||
*/
|
||||
virtual void clear() = 0;
|
||||
|
||||
/**
|
||||
* Get an estimate of the amount of memory used for the set.
|
||||
*/
|
||||
virtual std::size_t used_memory() const noexcept = 0;
|
||||
|
||||
}; // class IdSet
|
||||
|
||||
template <typename T>
|
||||
class IdSetDense;
|
||||
|
||||
/**
|
||||
* Const_iterator for iterating over a IdSetDense.
|
||||
*/
|
||||
template <typename T>
|
||||
class IdSetDenseIterator {
|
||||
|
||||
static_assert(std::is_unsigned<T>::value, "Needs unsigned type");
|
||||
static_assert(sizeof(T) >= 4, "Needs at least 32bit type");
|
||||
|
||||
const IdSetDense<T>* m_set;
|
||||
T m_value;
|
||||
T m_last;
|
||||
|
||||
void next() noexcept {
|
||||
while (m_value != m_last && !m_set->get(m_value)) {
|
||||
const T cid = IdSetDense<T>::chunk_id(m_value);
|
||||
assert(cid < m_set->m_data.size());
|
||||
if (!m_set->m_data[cid]) {
|
||||
m_value = (cid + 1) << (IdSetDense<T>::chunk_bits + 3);
|
||||
} else {
|
||||
const auto slot = m_set->m_data[cid][IdSetDense<T>::offset(m_value)];
|
||||
if (slot == 0) {
|
||||
m_value += 8;
|
||||
m_value &= ~0x7;
|
||||
} else {
|
||||
++m_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = T;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
IdSetDenseIterator(const IdSetDense<T>* set, T value, T last) noexcept :
|
||||
m_set(set),
|
||||
m_value(value),
|
||||
m_last(last) {
|
||||
next();
|
||||
}
|
||||
|
||||
IdSetDenseIterator<T>& operator++() noexcept {
|
||||
if (m_value != m_last) {
|
||||
++m_value;
|
||||
next();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
IdSetDenseIterator<T> operator++(int) noexcept {
|
||||
IdSetDenseIterator<T> tmp{*this};
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const IdSetDenseIterator<T>& rhs) const noexcept {
|
||||
return m_set == rhs.m_set && m_value == rhs.m_value;
|
||||
}
|
||||
|
||||
bool operator!=(const IdSetDenseIterator<T>& rhs) const noexcept {
|
||||
return ! (*this == rhs);
|
||||
}
|
||||
|
||||
T operator*() const noexcept {
|
||||
assert(m_value < m_last);
|
||||
return m_value;
|
||||
}
|
||||
|
||||
}; // class IdSetDenseIterator
|
||||
|
||||
/**
|
||||
* A set of Ids of the given type. Internal storage is in chunks of
|
||||
* arrays used as bit fields. Internally those chunks will be allocated
|
||||
* as needed, so it works relatively efficiently with both smaller
|
||||
* and larger Id sets. If it is not used, no memory is allocated at
|
||||
* all.
|
||||
*/
|
||||
template <typename T>
|
||||
class IdSetDense : public IdSet<T> {
|
||||
|
||||
static_assert(std::is_unsigned<T>::value, "Needs unsigned type");
|
||||
static_assert(sizeof(T) >= 4, "Needs at least 32bit type");
|
||||
|
||||
friend class IdSetDenseIterator<T>;
|
||||
|
||||
// This value is a compromise. For node Ids it could be bigger
|
||||
// which would mean less (but larger) memory allocations. For
|
||||
// relations Ids it could be smaller, because they would all fit
|
||||
// into a smaller allocation.
|
||||
constexpr static const std::size_t chunk_bits = 22;
|
||||
constexpr static const std::size_t chunk_size = 1 << chunk_bits;
|
||||
|
||||
std::vector<std::unique_ptr<unsigned char[]>> m_data;
|
||||
T m_size = 0;
|
||||
|
||||
static std::size_t chunk_id(T id) noexcept {
|
||||
return id >> (chunk_bits + 3);
|
||||
}
|
||||
|
||||
static std::size_t offset(T id) noexcept {
|
||||
return (id >> 3) & ((1 << chunk_bits) - 1);
|
||||
}
|
||||
|
||||
static unsigned char bitmask(T id) noexcept {
|
||||
return 1 << (id & 0x7);
|
||||
}
|
||||
|
||||
T last() const noexcept {
|
||||
return static_cast<T>(m_data.size()) * chunk_size * 8;
|
||||
}
|
||||
|
||||
unsigned char& get_element(T id) {
|
||||
const auto cid = chunk_id(id);
|
||||
if (cid >= m_data.size()) {
|
||||
m_data.resize(cid + 1);
|
||||
}
|
||||
|
||||
auto& chunk = m_data[cid];
|
||||
if (!chunk) {
|
||||
chunk.reset(new unsigned char[chunk_size]);
|
||||
::memset(chunk.get(), 0, chunk_size);
|
||||
}
|
||||
|
||||
return chunk[offset(id)];
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
using const_iterator = IdSetDenseIterator<T>;
|
||||
|
||||
IdSetDense() = default;
|
||||
|
||||
/**
|
||||
* Add the Id to the set if it is not already in there.
|
||||
*
|
||||
* @param id The Id to set.
|
||||
* @returns true if the Id was added, false if it was already set.
|
||||
*/
|
||||
bool check_and_set(T id) {
|
||||
auto& element = get_element(id);
|
||||
|
||||
if ((element & bitmask(id)) == 0) {
|
||||
element |= bitmask(id);
|
||||
++m_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given Id to the set.
|
||||
*
|
||||
* @param id The Id to set.
|
||||
*/
|
||||
void set(T id) final {
|
||||
(void)check_and_set(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given Id from the set.
|
||||
*
|
||||
* @param id The Id to set.
|
||||
*/
|
||||
void unset(T id) {
|
||||
auto& element = get_element(id);
|
||||
|
||||
if ((element & bitmask(id)) != 0) {
|
||||
element &= ~bitmask(id);
|
||||
--m_size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the Id in the set?
|
||||
*
|
||||
* @param id The Id to check.
|
||||
*/
|
||||
bool get(T id) const noexcept final {
|
||||
if (chunk_id(id) >= m_data.size()) {
|
||||
return false;
|
||||
}
|
||||
auto* r = m_data[chunk_id(id)].get();
|
||||
if (!r) {
|
||||
return false;
|
||||
}
|
||||
return (r[offset(id)] & bitmask(id)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the set empty?
|
||||
*/
|
||||
bool empty() const noexcept final {
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of Ids stored in the set.
|
||||
*/
|
||||
T size() const noexcept {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the set.
|
||||
*/
|
||||
void clear() final {
|
||||
m_data.clear();
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
std::size_t used_memory() const noexcept final {
|
||||
return m_data.size() * chunk_size;
|
||||
}
|
||||
|
||||
IdSetDenseIterator<T> begin() const {
|
||||
return {this, 0, last()};
|
||||
}
|
||||
|
||||
IdSetDenseIterator<T> end() const {
|
||||
return {this, last(), last()};
|
||||
}
|
||||
|
||||
}; // class IdSetDense
|
||||
|
||||
/**
|
||||
* IdSet implementation for small Id sets. It writes the Ids
|
||||
* into a vector and uses linear search.
|
||||
*/
|
||||
template <typename T>
|
||||
class IdSetSmall : public IdSet<T> {
|
||||
|
||||
std::vector<T> m_data;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Add the given Id to the set.
|
||||
*/
|
||||
void set(T id) final {
|
||||
m_data.push_back(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the Id in the set? Uses linear search.
|
||||
*
|
||||
* @param id The Id to check.
|
||||
*/
|
||||
bool get(T id) const noexcept final {
|
||||
const auto it = std::find(m_data.cbegin(), m_data.cend(), id);
|
||||
return it != m_data.cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the Id in the set? Uses a binary search. For larger sets
|
||||
* this might be more efficient than calling get(), the set
|
||||
* must be sorted.
|
||||
*
|
||||
* @param id The Id to check.
|
||||
* @pre You must have called sort_unique() before calling this
|
||||
* or be sure there are no duplicates and the Ids have been
|
||||
* set in order.
|
||||
*/
|
||||
bool get_binary_search(T id) const noexcept {
|
||||
return std::binary_search(m_data.cbegin(), m_data.cend(), id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the set empty?
|
||||
*/
|
||||
bool empty() const noexcept final {
|
||||
return m_data.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the set.
|
||||
*/
|
||||
void clear() final {
|
||||
m_data.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the internal vector and remove any duplicates. Call this
|
||||
* before using size(), get_binary_search() or using an iterator.
|
||||
*/
|
||||
void sort_unique() {
|
||||
std::sort(m_data.begin(), m_data.end());
|
||||
const auto last = std::unique(m_data.begin(), m_data.end());
|
||||
m_data.erase(last, m_data.end());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of Ids stored in the set.
|
||||
*
|
||||
* @pre You must have called sort_unique() before calling this
|
||||
* or be sure there are no duplicates.
|
||||
*/
|
||||
std::size_t size() const noexcept {
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
std::size_t used_memory() const noexcept final {
|
||||
return m_data.capacity() * sizeof(T);
|
||||
}
|
||||
|
||||
/// Iterator type. There is no non-const iterator.
|
||||
using const_iterator = typename std::vector<T>::const_iterator;
|
||||
|
||||
const_iterator begin() const noexcept {
|
||||
return m_data.cbegin();
|
||||
}
|
||||
|
||||
const_iterator end() const noexcept {
|
||||
return m_data.cend();
|
||||
}
|
||||
|
||||
const_iterator cbegin() const noexcept {
|
||||
return m_data.cbegin();
|
||||
}
|
||||
|
||||
const_iterator cend() const noexcept {
|
||||
return m_data.cend();
|
||||
}
|
||||
|
||||
}; // class IdSetSmall
|
||||
|
||||
/// @deprecated Use nwr_array helper class instead.
|
||||
template <template<typename> class IdSetType>
|
||||
class NWRIdSet {
|
||||
|
||||
using id_set_type = IdSetType<osmium::unsigned_object_id_type>;
|
||||
|
||||
id_set_type m_sets[3];
|
||||
|
||||
public:
|
||||
|
||||
id_set_type& operator()(osmium::item_type type) noexcept {
|
||||
return m_sets[osmium::item_type_to_nwr_index(type)];
|
||||
}
|
||||
|
||||
const id_set_type& operator()(osmium::item_type type) const noexcept {
|
||||
return m_sets[osmium::item_type_to_nwr_index(type)];
|
||||
}
|
||||
|
||||
}; // class NWRIdSet
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_ID_SET_HPP
|
||||
@@ -0,0 +1,95 @@
|
||||
#ifndef OSMIUM_INDEX_INDEX_HPP
|
||||
#define OSMIUM_INDEX_INDEX_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* Exception signaling that an element could not be
|
||||
* found in an index.
|
||||
*/
|
||||
struct not_found : public std::runtime_error {
|
||||
|
||||
explicit not_found(const std::string& what) :
|
||||
std::runtime_error(what) {
|
||||
}
|
||||
|
||||
explicit not_found(const char* what) :
|
||||
std::runtime_error(what) {
|
||||
}
|
||||
|
||||
explicit not_found(uint64_t id) :
|
||||
std::runtime_error(std::string{"id "} + std::to_string(id) + " not found") {
|
||||
}
|
||||
|
||||
}; // struct not_found
|
||||
|
||||
/**
|
||||
* @brief Indexing of OSM data, Locations, etc.
|
||||
*/
|
||||
namespace index {
|
||||
|
||||
/**
|
||||
* Some of the index classes need an "empty" value that can
|
||||
* never appear in real data. This function must return this
|
||||
* empty value for any class used as a value in an index.
|
||||
* The default implementation returns a default constructed
|
||||
* object, but it can be specialized.
|
||||
*/
|
||||
template <typename T>
|
||||
inline constexpr T empty_value() {
|
||||
return T{};
|
||||
}
|
||||
|
||||
/**
|
||||
* The size_t value in indexes is usually used for offsets
|
||||
* into a buffer or file. It is unlikely that we ever need
|
||||
* the full range, so the max value is a good "empty" value.
|
||||
*/
|
||||
template <>
|
||||
inline constexpr size_t empty_value<size_t>() {
|
||||
return std::numeric_limits<size_t>::max();
|
||||
}
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_INDEX_HPP
|
||||
+296
@@ -0,0 +1,296 @@
|
||||
#ifndef OSMIUM_INDEX_MAP_HPP
|
||||
#define OSMIUM_INDEX_MAP_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/util/string.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
struct map_factory_error : public std::runtime_error {
|
||||
|
||||
explicit map_factory_error(const char* message) :
|
||||
std::runtime_error(message) {
|
||||
}
|
||||
|
||||
explicit map_factory_error(const std::string& message) :
|
||||
std::runtime_error(message) {
|
||||
}
|
||||
|
||||
}; // struct map_factory_error
|
||||
|
||||
namespace index {
|
||||
|
||||
/**
|
||||
* @brief Key-value containers with unique integer values for a key
|
||||
*/
|
||||
namespace map {
|
||||
|
||||
/**
|
||||
* This abstract class defines an interface to storage classes
|
||||
* intended for storing small pieces of data (such as coordinates)
|
||||
* indexed by a positive integer (such as an object ID). The
|
||||
* storage must be very space efficient and able to scale to billions
|
||||
* of objects.
|
||||
*
|
||||
* Subclasses have different implementations that store the
|
||||
* data in different ways in memory and/or on disk. Some storage
|
||||
* classes are better suited when working with the whole planet,
|
||||
* some are better for data extracts.
|
||||
*
|
||||
* Note that these classes are not required to track "empty" fields.
|
||||
* When reading data you have to be sure you have put something in
|
||||
* there before.
|
||||
*
|
||||
* A typical use for this and derived classes is storage of node
|
||||
* locations indexed by node ID. These indexes will only work
|
||||
* on 64 bit systems if used in this case. 32 bit systems just
|
||||
* can't address that much memory!
|
||||
*
|
||||
* @tparam TId Id type, usually osmium::unsigned_object_id_type,
|
||||
* must be an unsigned integral type.
|
||||
* @tparam TValue Value type, usually osmium::Location or size_t.
|
||||
* Copied by value, so should be "small" type.
|
||||
*/
|
||||
template <typename TId, typename TValue>
|
||||
class Map {
|
||||
|
||||
static_assert(std::is_integral<TId>::value && std::is_unsigned<TId>::value,
|
||||
"TId template parameter for class Map must be unsigned integral type");
|
||||
|
||||
Map(const Map&) = delete;
|
||||
Map& operator=(const Map&) = delete;
|
||||
|
||||
protected:
|
||||
|
||||
Map(Map&&) = default;
|
||||
Map& operator=(Map&&) = default;
|
||||
|
||||
public:
|
||||
|
||||
/// The "key" type, usually osmium::unsigned_object_id_type.
|
||||
using key_type = TId;
|
||||
|
||||
/// The "value" type, usually a Location or size_t.
|
||||
using value_type = TValue;
|
||||
|
||||
Map() = default;
|
||||
|
||||
virtual ~Map() noexcept = default;
|
||||
|
||||
virtual void reserve(const size_t) {
|
||||
// default implementation is empty
|
||||
}
|
||||
|
||||
/// Set the field with id to value.
|
||||
virtual void set(const TId id, const TValue value) = 0;
|
||||
|
||||
/**
|
||||
* Retrieve value by id.
|
||||
*
|
||||
* @param id The id to look for.
|
||||
* @returns Value.
|
||||
* @throws osmium::not_found if the id could not be found.
|
||||
*/
|
||||
virtual TValue get(const TId id) const = 0;
|
||||
|
||||
/**
|
||||
* Retrieve value by id.
|
||||
*
|
||||
* @param id The id to look for.
|
||||
* @returns Value or, if not found, the empty value as defined
|
||||
* by osmium::index::empty_value<TValue>() which is
|
||||
* usually the default constructed value of type
|
||||
* TValue.
|
||||
*/
|
||||
virtual TValue get_noexcept(const TId id) const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Get the approximate number of items in the storage. The storage
|
||||
* might allocate memory in blocks, so this size might not be
|
||||
* accurate. You can not use this to find out how much memory the
|
||||
* storage uses. Use used_memory() for that.
|
||||
*/
|
||||
virtual size_t size() const = 0;
|
||||
|
||||
/**
|
||||
* Get the memory used for this storage in bytes. Note that this
|
||||
* is not necessarily entirely accurate but an approximation.
|
||||
* For storage classes that store the data in memory, this is
|
||||
* the main memory used, for storage classes storing data on disk
|
||||
* this is the memory used on disk.
|
||||
*/
|
||||
virtual size_t used_memory() const = 0;
|
||||
|
||||
/**
|
||||
* Clear memory used for this storage. After this you can not
|
||||
* use the storage container any more.
|
||||
*/
|
||||
virtual void clear() = 0;
|
||||
|
||||
/**
|
||||
* Sort data in map. Call this after writing all data and
|
||||
* before reading. Not all implementations need this.
|
||||
*/
|
||||
virtual void sort() {
|
||||
// default implementation is empty
|
||||
}
|
||||
|
||||
// This function can usually be const in derived classes,
|
||||
// but not always. It could, for instance, sort internal data.
|
||||
// This is why it is not declared const here.
|
||||
virtual void dump_as_list(const int /*fd*/) {
|
||||
throw std::runtime_error{"can't dump as list"};
|
||||
}
|
||||
|
||||
// This function can usually be const in derived classes,
|
||||
// but not always. It could, for instance, sort internal data.
|
||||
// This is why it is not declared const here.
|
||||
virtual void dump_as_array(const int /*fd*/) {
|
||||
throw std::runtime_error{"can't dump as array"};
|
||||
}
|
||||
|
||||
}; // class Map
|
||||
|
||||
} // namespace map
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
class MapFactory {
|
||||
|
||||
public:
|
||||
|
||||
using id_type = TId;
|
||||
using value_type = TValue;
|
||||
using map_type = osmium::index::map::Map<id_type, value_type>;
|
||||
using create_map_func = std::function<map_type*(const std::vector<std::string>&)>;
|
||||
|
||||
private:
|
||||
|
||||
std::map<const std::string, create_map_func> m_callbacks;
|
||||
|
||||
MapFactory() = default;
|
||||
|
||||
MapFactory(const MapFactory&) = delete;
|
||||
MapFactory& operator=(const MapFactory&) = delete;
|
||||
|
||||
MapFactory(MapFactory&&) = delete;
|
||||
MapFactory& operator=(MapFactory&&) = delete;
|
||||
|
||||
public:
|
||||
|
||||
static MapFactory<id_type, value_type>& instance() {
|
||||
static MapFactory<id_type, value_type> factory;
|
||||
return factory;
|
||||
}
|
||||
|
||||
bool register_map(const std::string& map_type_name, create_map_func func) {
|
||||
return m_callbacks.emplace(map_type_name, func).second;
|
||||
}
|
||||
|
||||
bool has_map_type(const std::string& map_type_name) const {
|
||||
return m_callbacks.count(map_type_name) != 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> map_types() const {
|
||||
std::vector<std::string> result;
|
||||
|
||||
for (const auto& cb : m_callbacks) {
|
||||
result.push_back(cb.first);
|
||||
}
|
||||
|
||||
std::sort(result.begin(), result.end());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<map_type> create_map(const std::string& config_string) const {
|
||||
std::vector<std::string> config{osmium::split_string(config_string, ',')};
|
||||
|
||||
if (config.empty()) {
|
||||
throw map_factory_error{"Need non-empty map type name"};
|
||||
}
|
||||
|
||||
const auto it = m_callbacks.find(config[0]);
|
||||
if (it != m_callbacks.end()) {
|
||||
return std::unique_ptr<map_type>((it->second)(config));
|
||||
}
|
||||
|
||||
throw map_factory_error{std::string{"Support for map type '"} + config[0] + "' not compiled into this binary"};
|
||||
}
|
||||
|
||||
}; // class MapFactory
|
||||
|
||||
namespace map {
|
||||
|
||||
template <typename TId, typename TValue, template<typename, typename> class TMap>
|
||||
struct create_map {
|
||||
TMap<TId, TValue>* operator()(const std::vector<std::string>&) {
|
||||
return new TMap<TId, TValue>();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace map
|
||||
|
||||
template <typename TId, typename TValue, template<typename, typename> class TMap>
|
||||
inline bool register_map(const std::string& name) {
|
||||
return osmium::index::MapFactory<TId, TValue>::instance().register_map(name, [](const std::vector<std::string>& config) {
|
||||
return map::create_map<TId, TValue, TMap>()(config);
|
||||
});
|
||||
}
|
||||
|
||||
#define OSMIUM_CONCATENATE_DETAIL_(x, y) x##y
|
||||
#define OSMIUM_CONCATENATE_(x, y) OSMIUM_CONCATENATE_DETAIL_(x, y)
|
||||
|
||||
#define REGISTER_MAP(id, value, klass, name) \
|
||||
namespace osmium { namespace index { namespace detail { \
|
||||
const bool OSMIUM_CONCATENATE_(registered_, name) = osmium::index::register_map<id, value, klass>(#name); \
|
||||
inline bool OSMIUM_CONCATENATE_(get_registered_, name)() noexcept { \
|
||||
return OSMIUM_CONCATENATE_(registered_, name); \
|
||||
} \
|
||||
} } }
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_MAP_HPP
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef OSMIUM_INDEX_MAP_ALL_HPP
|
||||
#define OSMIUM_INDEX_MAP_ALL_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/index/map/dense_file_array.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/map/dense_mem_array.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/map/dense_mmap_array.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/map/dummy.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/map/flex_mem.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/map/sparse_file_array.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/map/sparse_mem_array.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/map/sparse_mem_map.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/map/sparse_mem_table.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/map/sparse_mmap_array.hpp> // IWYU pragma: keep
|
||||
|
||||
#endif // OSMIUM_INDEX_MAP_ALL_HPP
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef OSMIUM_INDEX_MAP_DENSE_FILE_ARRAY_HPP
|
||||
#define OSMIUM_INDEX_MAP_DENSE_FILE_ARRAY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/index/detail/mmap_vector_file.hpp>
|
||||
#include <osmium/index/detail/vector_map.hpp>
|
||||
#include <osmium/index/detail/create_map_with_fd.hpp>
|
||||
|
||||
#define OSMIUM_HAS_INDEX_MAP_DENSE_FILE_ARRAY
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace map {
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
using DenseFileArray = VectorBasedDenseMap<osmium::detail::mmap_vector_file<TValue>, TId, TValue>;
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
struct create_map<TId, TValue, DenseFileArray> {
|
||||
DenseFileArray<TId, TValue>* operator()(const std::vector<std::string>& config) {
|
||||
return osmium::index::detail::create_map_with_fd<DenseFileArray<TId, TValue>>(config);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace map
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseFileArray, dense_file_array)
|
||||
#endif
|
||||
|
||||
#endif // OSMIUM_INDEX_MAP_DENSE_FILE_ARRAY_HPP
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef OSMIUM_INDEX_MAP_DENSE_MEM_ARRAY_HPP
|
||||
#define OSMIUM_INDEX_MAP_DENSE_MEM_ARRAY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/index/detail/vector_map.hpp>
|
||||
|
||||
#define OSMIUM_HAS_INDEX_MAP_DENSE_MEM_ARRAY
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace map {
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
using DenseMemArray = VectorBasedDenseMap<std::vector<TValue>, TId, TValue>;
|
||||
|
||||
} // namespace map
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseMemArray, dense_mem_array)
|
||||
#endif
|
||||
|
||||
#endif // OSMIUM_INDEX_MAP_DENSE_MEM_ARRAY_HPP
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef OSMIUM_INDEX_MAP_DENSE_MMAP_ARRAY_HPP
|
||||
#define OSMIUM_INDEX_MAP_DENSE_MMAP_ARRAY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <osmium/index/detail/mmap_vector_anon.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/detail/vector_map.hpp>
|
||||
|
||||
#define OSMIUM_HAS_INDEX_MAP_DENSE_MMAP_ARRAY
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace map {
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
using DenseMmapArray = VectorBasedDenseMap<osmium::detail::mmap_vector_anon<TValue>, TId, TValue>;
|
||||
|
||||
} // namespace map
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseMmapArray, dense_mmap_array)
|
||||
#endif
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
#endif // OSMIUM_INDEX_MAP_DENSE_MMAP_ARRAY_HPP
|
||||
@@ -0,0 +1,92 @@
|
||||
#ifndef OSMIUM_INDEX_MAP_DUMMY_HPP
|
||||
#define OSMIUM_INDEX_MAP_DUMMY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <osmium/index/index.hpp>
|
||||
#include <osmium/index/map.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace map {
|
||||
|
||||
/**
|
||||
* Pseudo map.
|
||||
* Use this class if you don't need a map, but you
|
||||
* need an object that behaves like one.
|
||||
*/
|
||||
template <typename TId, typename TValue>
|
||||
class Dummy : public osmium::index::map::Map<TId, TValue> {
|
||||
|
||||
public:
|
||||
|
||||
Dummy() = default;
|
||||
|
||||
~Dummy() noexcept final = default;
|
||||
|
||||
void set(const TId, const TValue) final {
|
||||
// intentionally left blank
|
||||
}
|
||||
|
||||
TValue get(const TId id) const final {
|
||||
throw osmium::not_found{id};
|
||||
}
|
||||
|
||||
TValue get_noexcept(const TId /*id*/) const noexcept final {
|
||||
return osmium::index::empty_value<TValue>();
|
||||
}
|
||||
|
||||
size_t size() const final {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t used_memory() const final {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void clear() final {
|
||||
}
|
||||
|
||||
}; // class Dummy
|
||||
|
||||
} // namespace map
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_MAP_DUMMY_HPP
|
||||
@@ -0,0 +1,285 @@
|
||||
#ifndef OSMIUM_INDEX_MAP_FLEX_MEM_HPP
|
||||
#define OSMIUM_INDEX_MAP_FLEX_MEM_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/index/map.hpp>
|
||||
#include <osmium/index/index.hpp>
|
||||
|
||||
#define OSMIUM_HAS_INDEX_MAP_FLEX_MEM
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace map {
|
||||
|
||||
/**
|
||||
* This is an autoscaling index that works well with small and
|
||||
* large input data. All data will be held in memory. For small
|
||||
* input data a sparse array will be used, if this becomes
|
||||
* inefficient, the class will switch automatically to a dense
|
||||
* index.
|
||||
*/
|
||||
template <typename TId, typename TValue>
|
||||
class FlexMem : public osmium::index::map::Map<TId, TValue> {
|
||||
|
||||
// This value is based on benchmarks with a planet file and
|
||||
// some smaller files.
|
||||
enum constant_bits {
|
||||
bits = 16
|
||||
};
|
||||
|
||||
enum constant_block_size : uint64_t {
|
||||
block_size = 1ll << bits
|
||||
};
|
||||
|
||||
// Minimum number of entries in the sparse index before we
|
||||
// are considering switching to a dense index.
|
||||
enum constant_min_dense_entries : int64_t {
|
||||
min_dense_entries = 0xffffff
|
||||
};
|
||||
|
||||
// When more than a third of all Ids are in the index, we
|
||||
// switch to the dense index. This is a compromise between
|
||||
// the best memory efficiency (which we would get at a factor
|
||||
// of 2) and the performance (dense index is much faster then
|
||||
// the sparse index).
|
||||
enum constant_density_factor {
|
||||
density_factor = 3
|
||||
};
|
||||
|
||||
// An entry in the sparse index
|
||||
struct entry {
|
||||
uint64_t id;
|
||||
TValue value;
|
||||
|
||||
entry(uint64_t i, TValue v) :
|
||||
id(i),
|
||||
value(v) {
|
||||
}
|
||||
|
||||
bool operator<(const entry other) const noexcept {
|
||||
return id < other.id;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<entry> m_sparse_entries;
|
||||
|
||||
std::vector<std::vector<TValue>> m_dense_blocks;
|
||||
|
||||
// The maximum Id that was seen yet. Only set in sparse mode.
|
||||
uint64_t m_max_id = 0;
|
||||
|
||||
// Set to false in sparse mode and to true in dense mode.
|
||||
bool m_dense;
|
||||
|
||||
static uint64_t block(const uint64_t id) noexcept {
|
||||
return id >> bits;
|
||||
}
|
||||
|
||||
static uint64_t offset(const uint64_t id) noexcept {
|
||||
return id & (block_size - 1);
|
||||
}
|
||||
|
||||
// Assure that the block with the given number exists. Create
|
||||
// it if needed.
|
||||
void assure_block(const uint64_t num) {
|
||||
if (num >= m_dense_blocks.size()) {
|
||||
m_dense_blocks.resize(num + 1);
|
||||
}
|
||||
if (m_dense_blocks[num].empty()) {
|
||||
m_dense_blocks[num].assign(block_size, osmium::index::empty_value<TValue>());
|
||||
}
|
||||
}
|
||||
|
||||
void set_sparse(const uint64_t id, const TValue value) {
|
||||
m_sparse_entries.emplace_back(id, value);
|
||||
if (id > m_max_id) {
|
||||
m_max_id = id;
|
||||
|
||||
if (m_sparse_entries.size() >= min_dense_entries) {
|
||||
if (m_max_id < m_sparse_entries.size() * density_factor) {
|
||||
switch_to_dense();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TValue get_sparse(const uint64_t id) const noexcept {
|
||||
const auto it = std::lower_bound(m_sparse_entries.begin(),
|
||||
m_sparse_entries.end(),
|
||||
entry{id, osmium::index::empty_value<TValue>()});
|
||||
if (it == m_sparse_entries.end() || it->id != id) {
|
||||
return osmium::index::empty_value<TValue>();
|
||||
}
|
||||
return it->value;
|
||||
}
|
||||
|
||||
void set_dense(const uint64_t id, const TValue value) {
|
||||
assure_block(block(id));
|
||||
m_dense_blocks[block(id)][offset(id)] = value;
|
||||
}
|
||||
|
||||
TValue get_dense(const uint64_t id) const noexcept {
|
||||
if (m_dense_blocks.size() <= block(id) || m_dense_blocks[block(id)].empty()) {
|
||||
return osmium::index::empty_value<TValue>();
|
||||
}
|
||||
return m_dense_blocks[block(id)][offset(id)];
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Create FlexMem index.
|
||||
*
|
||||
* @param use_dense Usually FlexMem indexes start out as sparse
|
||||
* indexes and will switch to dense when they
|
||||
* think it is better. Set this to force dense
|
||||
* indexing from the start. This is usually
|
||||
* only useful for testing.
|
||||
*/
|
||||
explicit FlexMem(bool use_dense = false) :
|
||||
m_dense(use_dense) {
|
||||
}
|
||||
|
||||
~FlexMem() noexcept final = default;
|
||||
|
||||
bool is_dense() const noexcept {
|
||||
return m_dense;
|
||||
}
|
||||
|
||||
std::size_t size() const noexcept final {
|
||||
if (m_dense) {
|
||||
return m_dense_blocks.size() * block_size;
|
||||
}
|
||||
return m_sparse_entries.size();
|
||||
}
|
||||
|
||||
std::size_t used_memory() const noexcept final {
|
||||
return sizeof(FlexMem) +
|
||||
m_sparse_entries.size() * sizeof(entry) +
|
||||
m_dense_blocks.size() * (block_size * sizeof(TValue) + sizeof(std::vector<TValue>));
|
||||
}
|
||||
|
||||
void set(const TId id, const TValue value) final {
|
||||
if (m_dense) {
|
||||
set_dense(id, value);
|
||||
} else {
|
||||
set_sparse(id, value);
|
||||
}
|
||||
}
|
||||
|
||||
TValue get_noexcept(const TId id) const noexcept final {
|
||||
if (m_dense) {
|
||||
return get_dense(id);
|
||||
}
|
||||
return get_sparse(id);
|
||||
}
|
||||
|
||||
TValue get(const TId id) const final {
|
||||
const auto value = get_noexcept(id);
|
||||
if (value == osmium::index::empty_value<TValue>()) {
|
||||
throw osmium::not_found{id};
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void clear() final {
|
||||
m_sparse_entries.clear();
|
||||
m_sparse_entries.shrink_to_fit();
|
||||
m_dense_blocks.clear();
|
||||
m_dense_blocks.shrink_to_fit();
|
||||
m_max_id = 0;
|
||||
m_dense = false;
|
||||
}
|
||||
|
||||
void sort() final {
|
||||
std::sort(m_sparse_entries.begin(), m_sparse_entries.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch from using a sparse to a dense index. Usually you
|
||||
* do not need to call this, because the FlexMem class will
|
||||
* do this automatically if it thinks the dense index is more
|
||||
* efficient.
|
||||
*
|
||||
* Does nothing if the index is already in dense mode.
|
||||
*/
|
||||
void switch_to_dense() {
|
||||
if (m_dense) {
|
||||
return;
|
||||
}
|
||||
for (const auto entry : m_sparse_entries) {
|
||||
set_dense(entry.id, entry.value);
|
||||
}
|
||||
m_sparse_entries.clear();
|
||||
m_sparse_entries.shrink_to_fit();
|
||||
m_max_id = 0;
|
||||
m_dense = true;
|
||||
}
|
||||
|
||||
std::pair<std::size_t, std::size_t> stats() const noexcept {
|
||||
std::size_t used_blocks = 0;
|
||||
std::size_t empty_blocks = 0;
|
||||
|
||||
for (const auto& block : m_dense_blocks) {
|
||||
if (block.empty()) {
|
||||
++empty_blocks;
|
||||
} else {
|
||||
++used_blocks;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(used_blocks, empty_blocks);
|
||||
}
|
||||
|
||||
}; // class FlexMem
|
||||
|
||||
} // namespace map
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::FlexMem, flex_mem)
|
||||
#endif
|
||||
|
||||
#endif // OSMIUM_INDEX_MAP_FLEX_MEM_HPP
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef OSMIUM_INDEX_MAP_SPARSE_FILE_ARRAY_HPP
|
||||
#define OSMIUM_INDEX_MAP_SPARSE_FILE_ARRAY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/index/detail/mmap_vector_file.hpp>
|
||||
#include <osmium/index/detail/vector_map.hpp>
|
||||
#include <osmium/index/detail/create_map_with_fd.hpp>
|
||||
|
||||
#define OSMIUM_HAS_INDEX_MAP_SPARSE_FILE_ARRAY
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace map {
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
using SparseFileArray = VectorBasedSparseMap<TId, TValue, osmium::detail::mmap_vector_file>;
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
struct create_map<TId, TValue, SparseFileArray> {
|
||||
SparseFileArray<TId, TValue>* operator()(const std::vector<std::string>& config) {
|
||||
return osmium::index::detail::create_map_with_fd<SparseFileArray<TId, TValue>>(config);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace map
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseFileArray, sparse_file_array)
|
||||
#endif
|
||||
|
||||
#endif // OSMIUM_INDEX_MAP_SPARSE_FILE_ARRAY_HPP
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef OSMIUM_INDEX_MAP_SPARSE_MEM_ARRAY_HPP
|
||||
#define OSMIUM_INDEX_MAP_SPARSE_MEM_ARRAY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/index/detail/vector_map.hpp>
|
||||
|
||||
#define OSMIUM_HAS_INDEX_MAP_SPARSE_MEM_ARRAY
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace map {
|
||||
|
||||
template <typename T>
|
||||
using StdVectorWrap = std::vector<T>;
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
using SparseMemArray = VectorBasedSparseMap<TId, TValue, StdVectorWrap>;
|
||||
|
||||
} // namespace map
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMemArray, sparse_mem_array)
|
||||
#endif
|
||||
|
||||
#endif // OSMIUM_INDEX_MAP_SPARSE_MEM_ARRAY_HPP
|
||||
@@ -0,0 +1,127 @@
|
||||
#ifndef OSMIUM_INDEX_MAP_SPARSE_MEM_MAP_HPP
|
||||
#define OSMIUM_INDEX_MAP_SPARSE_MEM_MAP_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm> // IWYU pragma: keep (for std::copy)
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/index/map.hpp>
|
||||
#include <osmium/index/index.hpp>
|
||||
#include <osmium/io/detail/read_write.hpp>
|
||||
|
||||
#define OSMIUM_HAS_INDEX_MAP_SPARSE_MEM_MAP
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace map {
|
||||
|
||||
/**
|
||||
* This implementation uses std::map internally. It uses rather a
|
||||
* lot of memory, but might make sense for small maps.
|
||||
*/
|
||||
template <typename TId, typename TValue>
|
||||
class SparseMemMap : public osmium::index::map::Map<TId, TValue> {
|
||||
|
||||
// This is a rough estimate for the memory needed for each
|
||||
// element in the map (id + value + pointers to left, right,
|
||||
// and parent plus some overhead for color of red-black-tree
|
||||
// or similar).
|
||||
static constexpr size_t element_size = sizeof(TId) + sizeof(TValue) + sizeof(void*) * 4;
|
||||
|
||||
std::map<TId, TValue> m_elements;
|
||||
|
||||
public:
|
||||
|
||||
SparseMemMap() = default;
|
||||
|
||||
~SparseMemMap() noexcept final = default;
|
||||
|
||||
void set(const TId id, const TValue value) final {
|
||||
m_elements[id] = value;
|
||||
}
|
||||
|
||||
TValue get(const TId id) const final {
|
||||
const auto it = m_elements.find(id);
|
||||
if (it == m_elements.end()) {
|
||||
throw osmium::not_found{id};
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
TValue get_noexcept(const TId id) const noexcept final {
|
||||
const auto it = m_elements.find(id);
|
||||
if (it == m_elements.end()) {
|
||||
return osmium::index::empty_value<TValue>();
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
size_t size() const noexcept final {
|
||||
return m_elements.size();
|
||||
}
|
||||
|
||||
size_t used_memory() const noexcept final {
|
||||
return element_size * m_elements.size();
|
||||
}
|
||||
|
||||
void clear() final {
|
||||
m_elements.clear();
|
||||
}
|
||||
|
||||
void dump_as_list(const int fd) final {
|
||||
using t = typename std::map<TId, TValue>::value_type;
|
||||
std::vector<t> v;
|
||||
v.reserve(m_elements.size());
|
||||
std::copy(m_elements.cbegin(), m_elements.cend(), std::back_inserter(v));
|
||||
osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(v.data()), sizeof(t) * v.size());
|
||||
}
|
||||
|
||||
}; // class SparseMemMap
|
||||
|
||||
} // namespace map
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMemMap, sparse_mem_map)
|
||||
#endif
|
||||
|
||||
#endif // OSMIUM_INDEX_MAP_SPARSE_MEM_MAP_HPP
|
||||
@@ -0,0 +1,159 @@
|
||||
#ifndef OSMIUM_INDEX_MAP_SPARSE_MEM_TABLE_HPP
|
||||
#define OSMIUM_INDEX_MAP_SPARSE_MEM_TABLE_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef OSMIUM_WITH_SPARSEHASH
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <google/sparsetable>
|
||||
|
||||
#include <osmium/index/index.hpp>
|
||||
#include <osmium/index/map.hpp>
|
||||
#include <osmium/io/detail/read_write.hpp>
|
||||
|
||||
#define OSMIUM_HAS_INDEX_MAP_SPARSE_MEM_TABLE
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace map {
|
||||
|
||||
/**
|
||||
* The SparseMemTable index stores elements in a Google sparsetable,
|
||||
* a data structure that can hold sparsly filled tables in a
|
||||
* space efficient way. It will resize automatically.
|
||||
*
|
||||
* Use this index if the ID space is only sparsly
|
||||
* populated, such as when working with smaller OSM files (like
|
||||
* country extracts).
|
||||
*
|
||||
* This will only work on 64 bit machines.
|
||||
*/
|
||||
template <typename TId, typename TValue>
|
||||
class SparseMemTable : public osmium::index::map::Map<TId, TValue> {
|
||||
|
||||
TId m_grow_size;
|
||||
|
||||
google::sparsetable<TValue> m_elements;
|
||||
|
||||
static_assert(sizeof(typename google::sparsetable<TValue>::size_type) >= 8, "google::sparsetable needs 64bit machine");
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param grow_size The initial size of the index (ie number of
|
||||
* elements that fit into the index).
|
||||
* The storage will grow by at least this size
|
||||
* every time it runs out of space.
|
||||
*/
|
||||
explicit SparseMemTable(const TId grow_size = 10000) :
|
||||
m_grow_size(grow_size),
|
||||
m_elements(grow_size) {
|
||||
}
|
||||
|
||||
~SparseMemTable() noexcept final = default;
|
||||
|
||||
void set(const TId id, const TValue value) final {
|
||||
if (id >= m_elements.size()) {
|
||||
m_elements.resize(id + m_grow_size);
|
||||
}
|
||||
m_elements[id] = value;
|
||||
}
|
||||
|
||||
TValue get(const TId id) const final {
|
||||
if (id >= m_elements.size()) {
|
||||
throw osmium::not_found{id};
|
||||
}
|
||||
const TValue value = m_elements[id];
|
||||
if (value == osmium::index::empty_value<TValue>()) {
|
||||
throw osmium::not_found{id};
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
TValue get_noexcept(const TId id) const noexcept final {
|
||||
if (id >= m_elements.size()) {
|
||||
return osmium::index::empty_value<TValue>();
|
||||
}
|
||||
return m_elements[id];
|
||||
}
|
||||
|
||||
size_t size() const final {
|
||||
return m_elements.size();
|
||||
}
|
||||
|
||||
size_t used_memory() const final {
|
||||
// unused elements use 1 bit, used elements sizeof(TValue) bytes
|
||||
// http://google-sparsehash.googlecode.com/svn/trunk/doc/sparsetable.html
|
||||
return (m_elements.size() / 8) + (m_elements.num_nonempty() * sizeof(TValue));
|
||||
}
|
||||
|
||||
void clear() final {
|
||||
m_elements.clear();
|
||||
}
|
||||
|
||||
void dump_as_list(const int fd) final {
|
||||
std::vector<std::pair<TId, TValue>> v;
|
||||
v.reserve(m_elements.size());
|
||||
int n = 0;
|
||||
for (const TValue value : m_elements) {
|
||||
if (value != osmium::index::empty_value<TValue>()) {
|
||||
v.emplace_back(n, value);
|
||||
}
|
||||
++n;
|
||||
}
|
||||
osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(v.data()), sizeof(std::pair<TId, TValue>) * v.size());
|
||||
}
|
||||
|
||||
}; // class SparseMemTable
|
||||
|
||||
} // namespace map
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMemTable, sparse_mem_table)
|
||||
#endif
|
||||
|
||||
#endif // OSMIUM_WITH_SPARSEHASH
|
||||
|
||||
#endif // OSMIUM_INDEX_MAP_SPARSE_MEM_TABLE_HPP
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef OSMIUM_INDEX_MAP_SPARSE_MMAP_ARRAY_HPP
|
||||
#define OSMIUM_INDEX_MAP_SPARSE_MMAP_ARRAY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <osmium/index/detail/mmap_vector_anon.hpp>
|
||||
#include <osmium/index/detail/vector_map.hpp>
|
||||
|
||||
#define OSMIUM_HAS_INDEX_MAP_SPARSE_MMAP_ARRAY
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace map {
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
using SparseMmapArray = VectorBasedSparseMap<TId, TValue, osmium::detail::mmap_vector_anon>;
|
||||
|
||||
} // namespace map
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#ifdef OSMIUM_WANT_NODE_LOCATION_MAPS
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMmapArray, sparse_mmap_array)
|
||||
#endif
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
#endif // OSMIUM_INDEX_MAP_SPARSE_MMAP_ARRAY_HPP
|
||||
@@ -0,0 +1,127 @@
|
||||
#ifndef OSMIUM_INDEX_MULTIMAP_HPP
|
||||
#define OSMIUM_INDEX_MULTIMAP_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
/**
|
||||
* @brief Key-value containers with multiple values for an integer key
|
||||
*/
|
||||
namespace multimap {
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
class Multimap {
|
||||
|
||||
static_assert(std::is_integral<TId>::value && std::is_unsigned<TId>::value, "TId template parameter for class Multimap must be unsigned integral type");
|
||||
|
||||
using element_type = typename std::pair<TId, TValue>;
|
||||
|
||||
Multimap(const Multimap&) = delete;
|
||||
Multimap& operator=(const Multimap&) = delete;
|
||||
|
||||
protected:
|
||||
|
||||
Multimap(Multimap&&) = default;
|
||||
Multimap& operator=(Multimap&&) = default;
|
||||
|
||||
public:
|
||||
|
||||
/// The "key" type, usually osmium::unsigned_object_id_type.
|
||||
using key_type = TId;
|
||||
|
||||
/// The "value" type, usually a Location or size_t.
|
||||
using value_type = TValue;
|
||||
|
||||
Multimap() = default;
|
||||
|
||||
virtual ~Multimap() noexcept = default;
|
||||
|
||||
/// Set the field with id to value.
|
||||
virtual void set(const TId id, const TValue value) = 0;
|
||||
|
||||
using iterator = element_type*;
|
||||
|
||||
// virtual std::pair<iterator, iterator> get_all(const TId id) const = 0;
|
||||
|
||||
/**
|
||||
* Get the approximate number of items in the storage. The storage
|
||||
* might allocate memory in blocks, so this size might not be
|
||||
* accurate. You can not use this to find out how much memory the
|
||||
* storage uses. Use used_memory() for that.
|
||||
*/
|
||||
virtual size_t size() const = 0;
|
||||
|
||||
/**
|
||||
* Get the memory used for this storage in bytes. Note that this
|
||||
* is not necessarily entirely accurate but an approximation.
|
||||
* For storage classes that store the data in memory, this is
|
||||
* the main memory used, for storage classes storing data on disk
|
||||
* this is the memory used on disk.
|
||||
*/
|
||||
virtual size_t used_memory() const = 0;
|
||||
|
||||
/**
|
||||
* Clear memory used for this storage. After this you can not
|
||||
* use the storage container any more.
|
||||
*/
|
||||
virtual void clear() = 0;
|
||||
|
||||
/**
|
||||
* Sort data in map. Call this after writing all data and
|
||||
* before reading. Not all implementations need this.
|
||||
*/
|
||||
virtual void sort() {
|
||||
// default implementation is empty
|
||||
}
|
||||
|
||||
virtual void dump_as_list(const int /*fd*/) {
|
||||
std::runtime_error("can't dump as list");
|
||||
}
|
||||
|
||||
}; // class Multimap
|
||||
|
||||
} // namespace multimap
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_MULTIMAP_HPP
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef OSMIUM_INDEX_MULTIMAP_ALL_HPP
|
||||
#define OSMIUM_INDEX_MULTIMAP_ALL_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/index/multimap/sparse_file_array.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/multimap/sparse_mem_array.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/multimap/sparse_mem_multimap.hpp> // IWYU pragma: keep
|
||||
#include <osmium/index/multimap/sparse_mmap_array.hpp> // IWYU pragma: keep
|
||||
|
||||
#endif // OSMIUM_INDEX_MULTIMAP_ALL_HPP
|
||||
@@ -0,0 +1,204 @@
|
||||
#ifndef OSMIUM_INDEX_MULTIMAP_HYBRID_HPP
|
||||
#define OSMIUM_INDEX_MULTIMAP_HYBRID_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/index/index.hpp>
|
||||
#include <osmium/index/multimap.hpp>
|
||||
#include <osmium/index/multimap/sparse_mem_array.hpp>
|
||||
#include <osmium/index/multimap/sparse_mem_multimap.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace multimap {
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
class HybridIterator {
|
||||
|
||||
using main_map_type = SparseMemArray<TId, TValue>;
|
||||
using extra_map_type = SparseMemMultimap<TId, TValue>;
|
||||
|
||||
using element_type = typename std::pair<TId, TValue>;
|
||||
|
||||
typename main_map_type::iterator m_begin_main;
|
||||
typename main_map_type::iterator m_end_main;
|
||||
typename extra_map_type::iterator m_begin_extra;
|
||||
typename extra_map_type::iterator m_end_extra;
|
||||
|
||||
public:
|
||||
|
||||
HybridIterator(typename main_map_type::iterator begin_main,
|
||||
typename main_map_type::iterator end_main,
|
||||
typename extra_map_type::iterator begin_extra,
|
||||
typename extra_map_type::iterator end_extra) :
|
||||
m_begin_main(begin_main),
|
||||
m_end_main(end_main),
|
||||
m_begin_extra(begin_extra),
|
||||
m_end_extra(end_extra) {
|
||||
}
|
||||
|
||||
~HybridIterator() noexcept = default;
|
||||
|
||||
HybridIterator& operator++() {
|
||||
if (m_begin_main == m_end_main) {
|
||||
++m_begin_extra;
|
||||
} else {
|
||||
++m_begin_main;
|
||||
while (m_begin_main != m_end_main && m_begin_main->second == osmium::index::empty_value<TValue>()) { // ignore removed elements
|
||||
++m_begin_main;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
HybridIterator<TId, TValue> operator++(int) {
|
||||
auto tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const HybridIterator& rhs) const {
|
||||
return m_begin_main == rhs.m_begin_main &&
|
||||
m_end_main == rhs.m_end_main &&
|
||||
m_begin_extra == rhs.m_begin_extra &&
|
||||
m_end_extra == rhs.m_end_extra;
|
||||
}
|
||||
|
||||
bool operator!=(const HybridIterator& rhs) const {
|
||||
return ! operator==(rhs);
|
||||
}
|
||||
|
||||
const element_type& operator*() {
|
||||
if (m_begin_main == m_end_main) {
|
||||
return *m_begin_extra;
|
||||
} else {
|
||||
return *m_begin_main;
|
||||
}
|
||||
}
|
||||
|
||||
const element_type* operator->() {
|
||||
return &operator*();
|
||||
}
|
||||
|
||||
}; // class HybridIterator
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
class Hybrid : public Multimap<TId, TValue> {
|
||||
|
||||
using main_map_type = SparseMemArray<TId, TValue>;
|
||||
using extra_map_type = SparseMemMultimap<TId, TValue>;
|
||||
|
||||
main_map_type m_main;
|
||||
extra_map_type m_extra;
|
||||
|
||||
public:
|
||||
|
||||
using iterator = HybridIterator<TId, TValue>;
|
||||
using const_iterator = const HybridIterator<TId, TValue>;
|
||||
|
||||
Hybrid() :
|
||||
m_main(),
|
||||
m_extra() {
|
||||
}
|
||||
|
||||
~Hybrid() noexcept = default;
|
||||
|
||||
size_t size() const final {
|
||||
return m_main.size() + m_extra.size();
|
||||
}
|
||||
|
||||
size_t used_memory() const final {
|
||||
return m_main.used_memory() + m_extra.used_memory();
|
||||
}
|
||||
|
||||
void reserve(const size_t size) {
|
||||
m_main.reserve(size);
|
||||
}
|
||||
|
||||
void unsorted_set(const TId id, const TValue value) {
|
||||
m_main.set(id, value);
|
||||
}
|
||||
|
||||
void set(const TId id, const TValue value) final {
|
||||
m_extra.set(id, value);
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> get_all(const TId id) {
|
||||
auto result_main = m_main.get_all(id);
|
||||
auto result_extra = m_extra.get_all(id);
|
||||
return std::make_pair(iterator(result_main.first, result_main.second, result_extra.first, result_extra.second),
|
||||
iterator(result_main.second, result_main.second, result_extra.second, result_extra.second));
|
||||
}
|
||||
|
||||
void remove(const TId id, const TValue value) {
|
||||
m_main.remove(id, value);
|
||||
m_extra.remove(id, value);
|
||||
}
|
||||
|
||||
void consolidate() {
|
||||
m_main.erase_removed();
|
||||
for (const auto& element : m_extra) {
|
||||
m_main.set(element.first, element.second);
|
||||
}
|
||||
m_extra.clear();
|
||||
m_main.sort();
|
||||
}
|
||||
|
||||
void dump_as_list(const int fd) final {
|
||||
consolidate();
|
||||
m_main.dump_as_list(fd);
|
||||
}
|
||||
|
||||
void clear() final {
|
||||
m_main.clear();
|
||||
m_extra.clear();
|
||||
}
|
||||
|
||||
void sort() final {
|
||||
m_main.sort();
|
||||
}
|
||||
|
||||
}; // class Hybrid
|
||||
|
||||
} // namespace multimap
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_MULTIMAP_HYBRID_HPP
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef OSMIUM_INDEX_MULTIMAP_SPARSE_FILE_ARRAY_HPP
|
||||
#define OSMIUM_INDEX_MULTIMAP_SPARSE_FILE_ARRAY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/index/detail/mmap_vector_file.hpp>
|
||||
#include <osmium/index/detail/vector_multimap.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace multimap {
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
using SparseFileArray = VectorBasedSparseMultimap<TId, TValue, osmium::detail::mmap_vector_file>;
|
||||
|
||||
} // namespace multimap
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_MULTIMAP_SPARSE_FILE_ARRAY_HPP
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef OSMIUM_INDEX_MULTIMAP_SPARSE_MEM_ARRAY_HPP
|
||||
#define OSMIUM_INDEX_MULTIMAP_SPARSE_MEM_ARRAY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/index/detail/vector_multimap.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace multimap {
|
||||
|
||||
template <typename T>
|
||||
using StdVectorWrap = std::vector<T>;
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
using SparseMemArray = VectorBasedSparseMultimap<TId, TValue, StdVectorWrap>;
|
||||
|
||||
} // namespace multimap
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_MULTIMAP_SPARSE_MEM_ARRAY_HPP
|
||||
@@ -0,0 +1,150 @@
|
||||
#ifndef OSMIUM_INDEX_MULTIMAP_SPARSE_MEM_MULTIMAP_HPP
|
||||
#define OSMIUM_INDEX_MULTIMAP_SPARSE_MEM_MULTIMAP_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/index/multimap.hpp>
|
||||
#include <osmium/io/detail/read_write.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace multimap {
|
||||
|
||||
/**
|
||||
* This implementation uses std::multimap internally. It uses rather a
|
||||
* lot of memory, but might make sense for small maps.
|
||||
*/
|
||||
template <typename TId, typename TValue>
|
||||
class SparseMemMultimap : public osmium::index::multimap::Multimap<TId, TValue> {
|
||||
|
||||
// This is a rough estimate for the memory needed for each
|
||||
// element in the map (id + value + pointers to left, right,
|
||||
// and parent plus some overhead for color of red-black-tree
|
||||
// or similar).
|
||||
static constexpr size_t element_size = sizeof(TId) + sizeof(TValue) + sizeof(void*) * 4;
|
||||
|
||||
public:
|
||||
|
||||
using collection_type = typename std::multimap<const TId, TValue>;
|
||||
using iterator = typename collection_type::iterator;
|
||||
using const_iterator = typename collection_type::const_iterator;
|
||||
using value_type = typename collection_type::value_type;
|
||||
using element_type = typename std::pair<TId, TValue>;
|
||||
|
||||
private:
|
||||
|
||||
collection_type m_elements;
|
||||
|
||||
public:
|
||||
|
||||
SparseMemMultimap() = default;
|
||||
|
||||
~SparseMemMultimap() noexcept final = default;
|
||||
|
||||
void unsorted_set(const TId id, const TValue value) {
|
||||
m_elements.emplace(id, value);
|
||||
}
|
||||
|
||||
void set(const TId id, const TValue value) final {
|
||||
m_elements.emplace(id, value);
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> get_all(const TId id) {
|
||||
return m_elements.equal_range(id);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> get_all(const TId id) const {
|
||||
return m_elements.equal_range(id);
|
||||
}
|
||||
|
||||
void remove(const TId id, const TValue value) {
|
||||
std::pair<iterator, iterator> r = get_all(id);
|
||||
for (iterator it = r.first; it != r.second; ++it) {
|
||||
if (it->second == value) {
|
||||
m_elements.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
return m_elements.begin();
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return m_elements.end();
|
||||
}
|
||||
|
||||
size_t size() const final {
|
||||
return m_elements.size();
|
||||
}
|
||||
|
||||
size_t used_memory() const final {
|
||||
return element_size * m_elements.size();
|
||||
}
|
||||
|
||||
void clear() final {
|
||||
m_elements.clear();
|
||||
}
|
||||
|
||||
void consolidate() {
|
||||
// intentionally left blank
|
||||
}
|
||||
|
||||
void dump_as_list(const int fd) final {
|
||||
std::vector<element_type> v;
|
||||
v.reserve(m_elements.size());
|
||||
for (const auto& element : m_elements) {
|
||||
v.emplace_back(element.first, element.second);
|
||||
}
|
||||
std::sort(v.begin(), v.end());
|
||||
osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(v.data()), sizeof(element_type) * v.size());
|
||||
}
|
||||
|
||||
}; // class SparseMemMultimap
|
||||
|
||||
} // namespace multimap
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_MULTIMAP_SPARSE_MEM_MULTIMAP_HPP
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef OSMIUM_INDEX_MULTIMAP_SPARSE_MMAP_ARRAY_HPP
|
||||
#define OSMIUM_INDEX_MULTIMAP_SPARSE_MMAP_ARRAY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <osmium/index/detail/mmap_vector_anon.hpp>
|
||||
#include <osmium/index/detail/vector_multimap.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace multimap {
|
||||
|
||||
template <typename TId, typename TValue>
|
||||
using SparseMmapArray = VectorBasedSparseMultimap<TId, TValue, osmium::detail::mmap_vector_anon>;
|
||||
|
||||
} // namespace multimap
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
#endif // OSMIUM_INDEX_MULTIMAP_SPARSE_MMAP_ARRAY_HPP
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifndef OSMIUM_INDEX_NODE_LOCATIONS_MAP_HPP
|
||||
#define OSMIUM_INDEX_NODE_LOCATIONS_MAP_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/index/map.hpp> // IWYU pragma: keep
|
||||
|
||||
#define OSMIUM_WANT_NODE_LOCATION_MAPS
|
||||
|
||||
#ifdef OSMIUM_HAS_INDEX_MAP_DENSE_FILE_ARRAY
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseFileArray, dense_file_array)
|
||||
#endif
|
||||
|
||||
#ifdef OSMIUM_HAS_INDEX_MAP_DENSE_MEM_ARRAY
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseMemArray, dense_mem_array)
|
||||
#endif
|
||||
|
||||
#ifdef OSMIUM_HAS_INDEX_MAP_DENSE_MMAP_ARRAY
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::DenseMmapArray, dense_mmap_array)
|
||||
#endif
|
||||
|
||||
#ifdef OSMIUM_HAS_INDEX_MAP_SPARSE_FILE_ARRAY
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseFileArray, sparse_file_array)
|
||||
#endif
|
||||
|
||||
#ifdef OSMIUM_HAS_INDEX_MAP_SPARSE_MEM_ARRAY
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMemArray, sparse_mem_array)
|
||||
#endif
|
||||
|
||||
#ifdef OSMIUM_HAS_INDEX_MAP_SPARSE_MEM_MAP
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMemMap, sparse_mem_map)
|
||||
#endif
|
||||
|
||||
#ifdef OSMIUM_HAS_INDEX_MAP_SPARSE_MEM_TABLE
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMemTable, sparse_mem_table)
|
||||
#endif
|
||||
|
||||
#ifdef OSMIUM_HAS_INDEX_MAP_SPARSE_MMAP_ARRAY
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::SparseMmapArray, sparse_mmap_array)
|
||||
#endif
|
||||
|
||||
#ifdef OSMIUM_HAS_INDEX_MAP_FLEX_MEM
|
||||
REGISTER_MAP(osmium::unsigned_object_id_type, osmium::Location, osmium::index::map::FlexMem, flex_mem)
|
||||
#endif
|
||||
|
||||
#endif // OSMIUM_INDEX_NODE_LOCATIONS_MAP_HPP
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef OSMIUM_INDEX_NWR_ARRAY_HPP
|
||||
#define OSMIUM_INDEX_NWR_ARRAY_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
template <typename T>
|
||||
class nwr_array {
|
||||
|
||||
T m_sets[3];
|
||||
|
||||
public:
|
||||
|
||||
T& operator()(osmium::item_type type) noexcept {
|
||||
return m_sets[osmium::item_type_to_nwr_index(type)];
|
||||
}
|
||||
|
||||
const T& operator()(osmium::item_type type) const noexcept {
|
||||
return m_sets[osmium::item_type_to_nwr_index(type)];
|
||||
}
|
||||
|
||||
}; // class nwr_array
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_NWR_ARRAY_HPP
|
||||
@@ -0,0 +1,438 @@
|
||||
#ifndef OSMIUM_INDEX_RELATIONS_MAP_HPP
|
||||
#define OSMIUM_INDEX_RELATIONS_MAP_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace index {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename TKey, typename TKeyInternal, typename TValue, typename TValueInternal>
|
||||
class flat_map {
|
||||
|
||||
public:
|
||||
|
||||
using key_type = TKey;
|
||||
using value_type = TValue;
|
||||
|
||||
private:
|
||||
|
||||
struct kv_pair {
|
||||
TKeyInternal key;
|
||||
TValueInternal value;
|
||||
|
||||
explicit kv_pair(key_type key_id) :
|
||||
key(static_cast<TKeyInternal>(key_id)),
|
||||
value() {
|
||||
}
|
||||
|
||||
kv_pair(key_type key_id, value_type value_id) :
|
||||
key(static_cast<TKeyInternal>(key_id)),
|
||||
value(static_cast<TValueInternal>(value_id)) {
|
||||
}
|
||||
|
||||
bool operator<(const kv_pair& other) const noexcept {
|
||||
return std::tie(key, value) < std::tie(other.key, other.value);
|
||||
}
|
||||
|
||||
bool operator==(const kv_pair& other) const noexcept {
|
||||
return std::tie(key, value) == std::tie(other.key, other.value);
|
||||
}
|
||||
}; // struct kv_pair
|
||||
|
||||
std::vector<kv_pair> m_map;
|
||||
|
||||
public:
|
||||
|
||||
using const_iterator = typename std::vector<kv_pair>::const_iterator;
|
||||
|
||||
void set(key_type key, value_type value) {
|
||||
m_map.emplace_back(key, value);
|
||||
}
|
||||
|
||||
typename std::enable_if<std::is_same<TKey, TValue>::value>::type flip_in_place() {
|
||||
for (auto& p : m_map) {
|
||||
using std::swap;
|
||||
swap(p.key, p.value);
|
||||
}
|
||||
}
|
||||
|
||||
flat_map<TValue, TValueInternal, TKey, TKeyInternal> flip_copy() {
|
||||
flat_map<TValue, TValueInternal, TKey, TKeyInternal> map;
|
||||
map.reserve(m_map.size());
|
||||
|
||||
for (const auto& p : m_map) {
|
||||
map.set(p.value, p.key);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void sort_unique() {
|
||||
std::sort(m_map.begin(), m_map.end());
|
||||
const auto last = std::unique(m_map.begin(), m_map.end());
|
||||
m_map.erase(last, m_map.end());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> get(key_type key) const noexcept {
|
||||
return std::equal_range(m_map.begin(), m_map.end(), kv_pair{key}, [](const kv_pair& lhs, const kv_pair& rhs) {
|
||||
return lhs.key < rhs.key;
|
||||
});
|
||||
}
|
||||
|
||||
bool empty() const noexcept {
|
||||
return m_map.empty();
|
||||
}
|
||||
|
||||
std::size_t size() const noexcept {
|
||||
return m_map.size();
|
||||
}
|
||||
|
||||
void reserve(std::size_t size) {
|
||||
m_map.reserve(size);
|
||||
}
|
||||
|
||||
}; // class flat_map
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Index for looking up parent relation IDs given a member relation ID
|
||||
* or the other way around.
|
||||
*
|
||||
* You can not instantiate such an index yourself, instead you need to
|
||||
* instantiate a RelationsMapStash, fill it and then create an index
|
||||
* from it:
|
||||
*
|
||||
* @code
|
||||
* RelationsMapStash stash;
|
||||
* ...
|
||||
* for_each_relation(const osmium::Relation& relation) {
|
||||
* stash.add_members(relation);
|
||||
* }
|
||||
* ...
|
||||
* const auto index = stash.build_member_to_parent_index();
|
||||
* ...
|
||||
* osmium::unsigned_object_id_type member_id = ...;
|
||||
* index.for_each(member_id, [](osmium::unsigned_object_id_type parent_id) {
|
||||
* ...
|
||||
* });
|
||||
* ...
|
||||
* @endcode
|
||||
*
|
||||
*/
|
||||
class RelationsMapIndex {
|
||||
|
||||
friend class RelationsMapStash;
|
||||
friend class RelationsMapIndexes;
|
||||
|
||||
using map_type = detail::flat_map<osmium::unsigned_object_id_type, uint32_t,
|
||||
osmium::unsigned_object_id_type, uint32_t>;
|
||||
|
||||
map_type m_map;
|
||||
|
||||
explicit RelationsMapIndex(map_type&& map) :
|
||||
m_map(std::move(map)) {
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
RelationsMapIndex() = delete;
|
||||
|
||||
RelationsMapIndex(const RelationsMapIndex&) = delete;
|
||||
RelationsMapIndex& operator=(const RelationsMapIndex&) = delete;
|
||||
|
||||
RelationsMapIndex(RelationsMapIndex&&) = default;
|
||||
RelationsMapIndex& operator=(RelationsMapIndex&&) = default;
|
||||
|
||||
/**
|
||||
* Find the given relation id in the index and call the given
|
||||
* function with all parent relation ids.
|
||||
*
|
||||
* @code
|
||||
* osmium::unsigned_object_id_type member_id = 17;
|
||||
* index.for_each_parent(member_id, [](osmium::unsigned_object_id_type id) {
|
||||
* ...
|
||||
* });
|
||||
* @endcode
|
||||
*
|
||||
* @deprecated Use for_each() instead.
|
||||
*
|
||||
* Complexity: Logarithmic in the number of elements in the index.
|
||||
* (Lookup uses binary search.)
|
||||
*/
|
||||
template <typename TFunc>
|
||||
void for_each_parent(osmium::unsigned_object_id_type member_id, TFunc&& func) const {
|
||||
const auto parents = m_map.get(member_id);
|
||||
for (auto it = parents.first; it != parents.second; ++it) {
|
||||
std::forward<TFunc>(func)(it->value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the given relation id in the index and call the given
|
||||
* function with all related relation ids.
|
||||
*
|
||||
* @code
|
||||
* osmium::unsigned_object_id_type id = 17;
|
||||
* index.for_each(id, [](osmium::unsigned_object_id_type rid) {
|
||||
* ...
|
||||
* });
|
||||
* @endcode
|
||||
*
|
||||
* Complexity: Logarithmic in the number of elements in the index.
|
||||
* (Lookup uses binary search.)
|
||||
*/
|
||||
template <typename TFunc>
|
||||
void for_each(osmium::unsigned_object_id_type id, TFunc&& func) const {
|
||||
const auto parents = m_map.get(id);
|
||||
for (auto it = parents.first; it != parents.second; ++it) {
|
||||
std::forward<TFunc>(func)(it->value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this index empty?
|
||||
*
|
||||
* Complexity: Constant.
|
||||
*/
|
||||
bool empty() const noexcept {
|
||||
return m_map.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* How many entries are in this index?
|
||||
*
|
||||
* Complexity: Constant.
|
||||
*/
|
||||
std::size_t size() const noexcept {
|
||||
return m_map.size();
|
||||
}
|
||||
|
||||
}; // class RelationsMapIndex
|
||||
|
||||
class RelationsMapIndexes {
|
||||
|
||||
friend class RelationsMapStash;
|
||||
|
||||
RelationsMapIndex m_member_to_parent;
|
||||
RelationsMapIndex m_parent_to_member;
|
||||
|
||||
RelationsMapIndexes(RelationsMapIndex::map_type&& map1, RelationsMapIndex::map_type&& map2) :
|
||||
m_member_to_parent(std::move(map1)),
|
||||
m_parent_to_member(std::move(map2)) {
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
const RelationsMapIndex& member_to_parent() const noexcept {
|
||||
return m_member_to_parent;
|
||||
}
|
||||
|
||||
const RelationsMapIndex& parent_to_member() const noexcept {
|
||||
return m_parent_to_member;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this index empty?
|
||||
*
|
||||
* Complexity: Constant.
|
||||
*/
|
||||
bool empty() const noexcept {
|
||||
return m_member_to_parent.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* How many entries are in this index?
|
||||
*
|
||||
* Complexity: Constant.
|
||||
*/
|
||||
std::size_t size() const noexcept {
|
||||
return m_member_to_parent.size();
|
||||
}
|
||||
|
||||
}; // class RelationsMapIndexes
|
||||
|
||||
/**
|
||||
* The RelationsMapStash is used to build up the data needed to create
|
||||
* an index of member relation ID to parent relation ID or the other
|
||||
* way around. See the RelationsMapIndex class for more.
|
||||
*/
|
||||
class RelationsMapStash {
|
||||
|
||||
using map_type = detail::flat_map<osmium::unsigned_object_id_type, uint32_t,
|
||||
osmium::unsigned_object_id_type, uint32_t>;
|
||||
|
||||
map_type m_map;
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool m_valid = true;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
RelationsMapStash() = default;
|
||||
|
||||
RelationsMapStash(const RelationsMapStash&) = delete;
|
||||
RelationsMapStash& operator=(const RelationsMapStash&) = delete;
|
||||
|
||||
RelationsMapStash(RelationsMapStash&&) = default;
|
||||
RelationsMapStash& operator=(RelationsMapStash&&) = default;
|
||||
|
||||
/**
|
||||
* Add mapping from member to parent relation in the stash.
|
||||
*/
|
||||
void add(osmium::unsigned_object_id_type member_id, osmium::unsigned_object_id_type relation_id) {
|
||||
assert(m_valid && "You can't use the RelationsMap any more after calling build_index()");
|
||||
m_map.set(member_id, relation_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add mapping from all members to given parent relation in the stash.
|
||||
*/
|
||||
void add_members(const osmium::Relation& relation) {
|
||||
assert(m_valid && "You can't use the RelationsMap any more after calling build_index()");
|
||||
for (const auto& member : relation.members()) {
|
||||
if (member.type() == osmium::item_type::relation) {
|
||||
m_map.set(member.positive_ref(), relation.positive_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this stash empty?
|
||||
*
|
||||
* Complexity: Constant.
|
||||
*/
|
||||
bool empty() const noexcept {
|
||||
assert(m_valid && "You can't use the RelationsMap any more after calling build_index()");
|
||||
return m_map.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* How many entries are in this stash?
|
||||
*
|
||||
* Complexity: Constant.
|
||||
*/
|
||||
std::size_t size() const noexcept {
|
||||
assert(m_valid && "You can't use the RelationsMap any more after calling build_index()");
|
||||
return m_map.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an index for member to parent lookups from the contents
|
||||
* of this stash and return it.
|
||||
*
|
||||
* After you get the index you can not use the stash any more!
|
||||
*
|
||||
* @deprecated Use build_member_to_parent_index() instead.
|
||||
*/
|
||||
RelationsMapIndex build_index() {
|
||||
assert(m_valid && "You can't use the RelationsMap any more after calling build_index()");
|
||||
m_map.sort_unique();
|
||||
#ifndef NDEBUG
|
||||
m_valid = false;
|
||||
#endif
|
||||
return RelationsMapIndex{std::move(m_map)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an index for member to parent lookups from the contents
|
||||
* of this stash and return it.
|
||||
*
|
||||
* After you get the index you can not use the stash any more!
|
||||
*/
|
||||
RelationsMapIndex build_member_to_parent_index() {
|
||||
assert(m_valid && "You can't use the RelationsMap any more after calling build_member_to_parent_index()");
|
||||
m_map.sort_unique();
|
||||
#ifndef NDEBUG
|
||||
m_valid = false;
|
||||
#endif
|
||||
return RelationsMapIndex{std::move(m_map)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an index for parent to member lookups from the contents
|
||||
* of this stash and return it.
|
||||
*
|
||||
* After you get the index you can not use the stash any more!
|
||||
*/
|
||||
RelationsMapIndex build_parent_to_member_index() {
|
||||
assert(m_valid && "You can't use the RelationsMap any more after calling build_parent_to_member_index()");
|
||||
m_map.flip_in_place();
|
||||
m_map.sort_unique();
|
||||
#ifndef NDEBUG
|
||||
m_valid = false;
|
||||
#endif
|
||||
return RelationsMapIndex{std::move(m_map)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build indexes for member-to-parent and parent-to-member lookups
|
||||
* from the contents of this stash and return them.
|
||||
*
|
||||
* After you get the index you can not use the stash any more!
|
||||
*/
|
||||
RelationsMapIndexes build_indexes() {
|
||||
assert(m_valid && "You can't use the RelationsMap any more after calling build_indexes()");
|
||||
auto reverse_map = m_map.flip_copy();
|
||||
reverse_map.sort_unique();
|
||||
m_map.sort_unique();
|
||||
#ifndef NDEBUG
|
||||
m_valid = false;
|
||||
#endif
|
||||
return RelationsMapIndexes{std::move(m_map), std::move(reverse_map)};
|
||||
}
|
||||
|
||||
}; // class RelationsMapStash
|
||||
|
||||
} // namespace index
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_INDEX_RELATIONS_MAP_HPP
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef OSMIUM_IO_ANY_COMPRESSION_HPP
|
||||
#define OSMIUM_IO_ANY_COMPRESSION_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Include this file if you want to read or write compressed OSM XML files.
|
||||
*
|
||||
* @attention If you include this file, you'll need to link with `libz`
|
||||
* and `libbz2`.
|
||||
*/
|
||||
|
||||
#include <osmium/io/bzip2_compression.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/gzip_compression.hpp> // IWYU pragma: export
|
||||
|
||||
#endif // OSMIUM_IO_ANY_COMPRESSION_HPP
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef OSMIUM_IO_ANY_INPUT_HPP
|
||||
#define OSMIUM_IO_ANY_INPUT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Include this file if you want to read all kinds of OSM files.
|
||||
*
|
||||
* @attention If you include this file, you'll need to link with
|
||||
* `ws2_32` (Windows only), `libexpat`, `libz`, `libbz2`,
|
||||
* and enable multithreading.
|
||||
*/
|
||||
|
||||
#include <osmium/io/any_compression.hpp> // IWYU pragma: export
|
||||
|
||||
#include <osmium/io/o5m_input.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/opl_input.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/pbf_input.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/xml_input.hpp> // IWYU pragma: export
|
||||
|
||||
#endif // OSMIUM_IO_ANY_INPUT_HPP
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef OSMIUM_IO_ANY_OUTPUT_HPP
|
||||
#define OSMIUM_IO_ANY_OUTPUT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Include this file if you want to write all kinds of OSM files.
|
||||
*
|
||||
* @attention If you include this file, you'll need to link with
|
||||
* `ws2_32` (Windows only), `libz`, `libbz2`, and enable
|
||||
* multithreading.
|
||||
*/
|
||||
|
||||
#include <osmium/io/any_compression.hpp> // IWYU pragma: export
|
||||
|
||||
#include <osmium/io/debug_output.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/opl_output.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/pbf_output.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/xml_output.hpp> // IWYU pragma: export
|
||||
|
||||
#endif // OSMIUM_IO_ANY_OUTPUT_HPP
|
||||
@@ -0,0 +1,323 @@
|
||||
#ifndef OSMIUM_IO_BZIP2_COMPRESSION_HPP
|
||||
#define OSMIUM_IO_BZIP2_COMPRESSION_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Include this file if you want to read or write bzip2-compressed OSM XML
|
||||
* files.
|
||||
*
|
||||
* @attention If you include this file, you'll need to link with `libbz2`.
|
||||
*/
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
#include <bzlib.h>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <osmium/io/compression.hpp>
|
||||
#include <osmium/io/detail/read_write.hpp>
|
||||
#include <osmium/io/error.hpp>
|
||||
#include <osmium/io/file_compression.hpp>
|
||||
#include <osmium/io/writer_options.hpp>
|
||||
#include <osmium/util/cast.hpp>
|
||||
#include <osmium/util/compatibility.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* Exception thrown when there are problems compressing or
|
||||
* decompressing bzip2 files.
|
||||
*/
|
||||
struct bzip2_error : public io_error {
|
||||
|
||||
int bzip2_error_code;
|
||||
int system_errno;
|
||||
|
||||
bzip2_error(const std::string& what, int error_code) :
|
||||
io_error(what),
|
||||
bzip2_error_code(error_code),
|
||||
system_errno(error_code == BZ_IO_ERROR ? errno : 0) {
|
||||
}
|
||||
|
||||
}; // struct bzip2_error
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
OSMIUM_NORETURN inline void throw_bzip2_error(BZFILE* bzfile, const char* msg, int bzlib_error = 0) {
|
||||
std::string error{"bzip2 error: "};
|
||||
error += msg;
|
||||
error += ": ";
|
||||
int errnum = bzlib_error;
|
||||
if (bzlib_error) {
|
||||
error += std::to_string(bzlib_error);
|
||||
} else {
|
||||
error += ::BZ2_bzerror(bzfile, &errnum);
|
||||
}
|
||||
throw osmium::bzip2_error{error, errnum};
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class Bzip2Compressor : public Compressor {
|
||||
|
||||
FILE* m_file;
|
||||
int m_bzerror;
|
||||
BZFILE* m_bzfile;
|
||||
|
||||
public:
|
||||
|
||||
explicit Bzip2Compressor(int fd, fsync sync) :
|
||||
Compressor(sync),
|
||||
m_file(fdopen(::dup(fd), "wb")),
|
||||
m_bzerror(BZ_OK),
|
||||
m_bzfile(::BZ2_bzWriteOpen(&m_bzerror, m_file, 6, 0, 0)) {
|
||||
if (!m_bzfile) {
|
||||
detail::throw_bzip2_error(m_bzfile, "write open failed", m_bzerror);
|
||||
}
|
||||
}
|
||||
|
||||
~Bzip2Compressor() noexcept final {
|
||||
try {
|
||||
close();
|
||||
} catch (...) {
|
||||
// Ignore any exceptions because destructor must not throw.
|
||||
}
|
||||
}
|
||||
|
||||
void write(const std::string& data) final {
|
||||
int error;
|
||||
::BZ2_bzWrite(&error, m_bzfile, const_cast<char*>(data.data()), static_cast_with_assert<int>(data.size()));
|
||||
if (error != BZ_OK && error != BZ_STREAM_END) {
|
||||
detail::throw_bzip2_error(m_bzfile, "write failed", error);
|
||||
}
|
||||
}
|
||||
|
||||
void close() final {
|
||||
if (m_bzfile) {
|
||||
int error;
|
||||
::BZ2_bzWriteClose(&error, m_bzfile, 0, nullptr, nullptr);
|
||||
m_bzfile = nullptr;
|
||||
if (m_file) {
|
||||
if (do_fsync()) {
|
||||
osmium::io::detail::reliable_fsync(::fileno(m_file));
|
||||
}
|
||||
if (fclose(m_file) != 0) {
|
||||
throw std::system_error{errno, std::system_category(), "Close failed"};
|
||||
}
|
||||
}
|
||||
if (error != BZ_OK) {
|
||||
detail::throw_bzip2_error(m_bzfile, "write close failed", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // class Bzip2Compressor
|
||||
|
||||
class Bzip2Decompressor : public Decompressor {
|
||||
|
||||
FILE* m_file;
|
||||
int m_bzerror;
|
||||
BZFILE* m_bzfile;
|
||||
bool m_stream_end {false};
|
||||
|
||||
public:
|
||||
|
||||
explicit Bzip2Decompressor(int fd) :
|
||||
Decompressor(),
|
||||
m_file(fdopen(::dup(fd), "rb")),
|
||||
m_bzerror(BZ_OK),
|
||||
m_bzfile(::BZ2_bzReadOpen(&m_bzerror, m_file, 0, 0, nullptr, 0)) {
|
||||
if (!m_bzfile) {
|
||||
detail::throw_bzip2_error(m_bzfile, "read open failed", m_bzerror);
|
||||
}
|
||||
}
|
||||
|
||||
~Bzip2Decompressor() noexcept final {
|
||||
try {
|
||||
close();
|
||||
} catch (...) {
|
||||
// Ignore any exceptions because destructor must not throw.
|
||||
}
|
||||
}
|
||||
|
||||
std::string read() final {
|
||||
std::string buffer;
|
||||
|
||||
if (!m_stream_end) {
|
||||
buffer.resize(osmium::io::Decompressor::input_buffer_size);
|
||||
int error;
|
||||
const int nread = ::BZ2_bzRead(&error, m_bzfile, const_cast<char*>(buffer.data()), static_cast_with_assert<int>(buffer.size()));
|
||||
if (error != BZ_OK && error != BZ_STREAM_END) {
|
||||
detail::throw_bzip2_error(m_bzfile, "read failed", error);
|
||||
}
|
||||
if (error == BZ_STREAM_END) {
|
||||
void* unused;
|
||||
int nunused;
|
||||
if (! feof(m_file)) {
|
||||
::BZ2_bzReadGetUnused(&error, m_bzfile, &unused, &nunused);
|
||||
if (error != BZ_OK) {
|
||||
detail::throw_bzip2_error(m_bzfile, "get unused failed", error);
|
||||
}
|
||||
std::string unused_data(static_cast<const char*>(unused), static_cast<std::string::size_type>(nunused));
|
||||
::BZ2_bzReadClose(&error, m_bzfile);
|
||||
if (error != BZ_OK) {
|
||||
detail::throw_bzip2_error(m_bzfile, "read close failed", error);
|
||||
}
|
||||
m_bzfile = ::BZ2_bzReadOpen(&error, m_file, 0, 0, const_cast<void*>(static_cast<const void*>(unused_data.data())), static_cast_with_assert<int>(unused_data.size()));
|
||||
if (error != BZ_OK) {
|
||||
detail::throw_bzip2_error(m_bzfile, "read open failed", error);
|
||||
}
|
||||
} else {
|
||||
m_stream_end = true;
|
||||
}
|
||||
}
|
||||
buffer.resize(static_cast<std::string::size_type>(nread));
|
||||
}
|
||||
|
||||
set_offset(size_t(ftell(m_file)));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void close() final {
|
||||
if (m_bzfile) {
|
||||
int error;
|
||||
::BZ2_bzReadClose(&error, m_bzfile);
|
||||
m_bzfile = nullptr;
|
||||
if (m_file) {
|
||||
if (fclose(m_file) != 0) {
|
||||
throw std::system_error{errno, std::system_category(), "Close failed"};
|
||||
}
|
||||
}
|
||||
if (error != BZ_OK) {
|
||||
detail::throw_bzip2_error(m_bzfile, "read close failed", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // class Bzip2Decompressor
|
||||
|
||||
class Bzip2BufferDecompressor : public Decompressor {
|
||||
|
||||
const char* m_buffer;
|
||||
size_t m_buffer_size;
|
||||
bz_stream m_bzstream;
|
||||
|
||||
public:
|
||||
|
||||
Bzip2BufferDecompressor(const char* buffer, size_t size) :
|
||||
m_buffer(buffer),
|
||||
m_buffer_size(size),
|
||||
m_bzstream() {
|
||||
m_bzstream.next_in = const_cast<char*>(buffer);
|
||||
m_bzstream.avail_in = static_cast_with_assert<unsigned int>(size);
|
||||
const int result = BZ2_bzDecompressInit(&m_bzstream, 0, 0);
|
||||
if (result != BZ_OK) {
|
||||
std::string message{"bzip2 error: decompression init failed: "};
|
||||
throw bzip2_error{message, result};
|
||||
}
|
||||
}
|
||||
|
||||
~Bzip2BufferDecompressor() noexcept final {
|
||||
try {
|
||||
close();
|
||||
} catch (...) {
|
||||
// Ignore any exceptions because destructor must not throw.
|
||||
}
|
||||
}
|
||||
|
||||
std::string read() final {
|
||||
std::string output;
|
||||
|
||||
if (m_buffer) {
|
||||
const size_t buffer_size = 10240;
|
||||
output.resize(buffer_size);
|
||||
m_bzstream.next_out = const_cast<char*>(output.data());
|
||||
m_bzstream.avail_out = buffer_size;
|
||||
const int result = BZ2_bzDecompress(&m_bzstream);
|
||||
|
||||
if (result != BZ_OK) {
|
||||
m_buffer = nullptr;
|
||||
m_buffer_size = 0;
|
||||
}
|
||||
|
||||
if (result != BZ_OK && result != BZ_STREAM_END) {
|
||||
std::string message{"bzip2 error: decompress failed: "};
|
||||
throw bzip2_error{message, result};
|
||||
}
|
||||
|
||||
output.resize(static_cast<unsigned long>(m_bzstream.next_out - output.data()));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void close() final {
|
||||
BZ2_bzDecompressEnd(&m_bzstream);
|
||||
}
|
||||
|
||||
}; // class Bzip2BufferDecompressor
|
||||
|
||||
namespace detail {
|
||||
|
||||
// we want the register_compression() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_bzip2_compression = osmium::io::CompressionFactory::instance().register_compression(osmium::io::file_compression::bzip2,
|
||||
[](int fd, fsync sync) { return new osmium::io::Bzip2Compressor{fd, sync}; },
|
||||
[](int fd) { return new osmium::io::Bzip2Decompressor{fd}; },
|
||||
[](const char* buffer, size_t size) { return new osmium::io::Bzip2BufferDecompressor{buffer, size}; }
|
||||
);
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_bzip2_compression() noexcept {
|
||||
return registered_bzip2_compression;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_BZIP2_COMPRESSION_HPP
|
||||
@@ -0,0 +1,337 @@
|
||||
#ifndef OSMIUM_IO_COMPRESSION_HPP
|
||||
#define OSMIUM_IO_COMPRESSION_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
#include <osmium/io/detail/read_write.hpp>
|
||||
#include <osmium/io/error.hpp>
|
||||
#include <osmium/io/file_compression.hpp>
|
||||
#include <osmium/io/writer_options.hpp>
|
||||
#include <osmium/util/file.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
class Compressor {
|
||||
|
||||
fsync m_fsync;
|
||||
|
||||
protected:
|
||||
|
||||
bool do_fsync() const {
|
||||
return m_fsync == fsync::yes;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit Compressor(fsync sync) :
|
||||
m_fsync(sync) {
|
||||
}
|
||||
|
||||
virtual ~Compressor() noexcept = default;
|
||||
|
||||
virtual void write(const std::string& data) = 0;
|
||||
|
||||
virtual void close() = 0;
|
||||
|
||||
}; // class Compressor
|
||||
|
||||
class Decompressor {
|
||||
|
||||
std::atomic<std::size_t> m_file_size{0};
|
||||
std::atomic<std::size_t> m_offset{0};
|
||||
|
||||
public:
|
||||
|
||||
static constexpr unsigned int input_buffer_size = 1024 * 1024;
|
||||
|
||||
Decompressor() = default;
|
||||
|
||||
Decompressor(const Decompressor&) = delete;
|
||||
Decompressor& operator=(const Decompressor&) = delete;
|
||||
|
||||
Decompressor(Decompressor&&) = delete;
|
||||
Decompressor& operator=(Decompressor&&) = delete;
|
||||
|
||||
virtual ~Decompressor() noexcept = default;
|
||||
|
||||
virtual std::string read() = 0;
|
||||
|
||||
virtual void close() = 0;
|
||||
|
||||
std::size_t file_size() const noexcept {
|
||||
return m_file_size;
|
||||
}
|
||||
|
||||
void set_file_size(std::size_t size) noexcept {
|
||||
m_file_size = size;
|
||||
}
|
||||
|
||||
std::size_t offset() const noexcept {
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
void set_offset(std::size_t offset) noexcept {
|
||||
m_offset = offset;
|
||||
}
|
||||
|
||||
}; // class Decompressor
|
||||
|
||||
/**
|
||||
* This singleton factory class is used to register compression
|
||||
* algorithms used for reading and writing OSM files.
|
||||
*
|
||||
* For each algorithm we store two functions that construct
|
||||
* a compressor and decompressor object, respectively.
|
||||
*/
|
||||
class CompressionFactory {
|
||||
|
||||
public:
|
||||
|
||||
using create_compressor_type = std::function<osmium::io::Compressor*(int, fsync)>;
|
||||
using create_decompressor_type_fd = std::function<osmium::io::Decompressor*(int)>;
|
||||
using create_decompressor_type_buffer = std::function<osmium::io::Decompressor*(const char*, std::size_t)>;
|
||||
|
||||
private:
|
||||
|
||||
using callbacks_type = std::tuple<create_compressor_type,
|
||||
create_decompressor_type_fd,
|
||||
create_decompressor_type_buffer>;
|
||||
|
||||
using compression_map_type = std::map<const osmium::io::file_compression, callbacks_type>;
|
||||
|
||||
compression_map_type m_callbacks;
|
||||
|
||||
CompressionFactory() = default;
|
||||
|
||||
CompressionFactory(const CompressionFactory&) = delete;
|
||||
CompressionFactory& operator=(const CompressionFactory&) = delete;
|
||||
|
||||
CompressionFactory(CompressionFactory&&) = delete;
|
||||
CompressionFactory& operator=(CompressionFactory&&) = delete;
|
||||
|
||||
const callbacks_type& find_callbacks(osmium::io::file_compression compression) const {
|
||||
const auto it = m_callbacks.find(compression);
|
||||
|
||||
if (it != m_callbacks.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::string error_message{"Support for compression '"};
|
||||
error_message += as_string(compression);
|
||||
error_message += "' not compiled into this binary";
|
||||
throw unsupported_file_format_error{error_message};
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static CompressionFactory& instance() {
|
||||
static CompressionFactory factory;
|
||||
return factory;
|
||||
}
|
||||
|
||||
bool register_compression(
|
||||
osmium::io::file_compression compression,
|
||||
create_compressor_type create_compressor,
|
||||
create_decompressor_type_fd create_decompressor_fd,
|
||||
create_decompressor_type_buffer create_decompressor_buffer) {
|
||||
|
||||
compression_map_type::value_type cc{compression,
|
||||
std::make_tuple(create_compressor,
|
||||
create_decompressor_fd,
|
||||
create_decompressor_buffer)};
|
||||
|
||||
return m_callbacks.insert(cc).second;
|
||||
}
|
||||
|
||||
template <typename... TArgs>
|
||||
std::unique_ptr<osmium::io::Compressor> create_compressor(osmium::io::file_compression compression, TArgs&&... args) const {
|
||||
const auto callbacks = find_callbacks(compression);
|
||||
return std::unique_ptr<osmium::io::Compressor>(std::get<0>(callbacks)(std::forward<TArgs>(args)...));
|
||||
}
|
||||
|
||||
std::unique_ptr<osmium::io::Decompressor> create_decompressor(osmium::io::file_compression compression, int fd) const {
|
||||
const auto callbacks = find_callbacks(compression);
|
||||
auto p = std::unique_ptr<osmium::io::Decompressor>(std::get<1>(callbacks)(fd));
|
||||
p->set_file_size(osmium::util::file_size(fd));
|
||||
return p;
|
||||
}
|
||||
|
||||
std::unique_ptr<osmium::io::Decompressor> create_decompressor(osmium::io::file_compression compression, const char* buffer, std::size_t size) const {
|
||||
const auto callbacks = find_callbacks(compression);
|
||||
return std::unique_ptr<osmium::io::Decompressor>(std::get<2>(callbacks)(buffer, size));
|
||||
}
|
||||
|
||||
}; // class CompressionFactory
|
||||
|
||||
class NoCompressor : public Compressor {
|
||||
|
||||
int m_fd;
|
||||
|
||||
public:
|
||||
|
||||
NoCompressor(int fd, fsync sync) :
|
||||
Compressor(sync),
|
||||
m_fd(fd) {
|
||||
}
|
||||
|
||||
~NoCompressor() noexcept final {
|
||||
try {
|
||||
close();
|
||||
} catch (...) {
|
||||
// Ignore any exceptions because destructor must not throw.
|
||||
}
|
||||
}
|
||||
|
||||
void write(const std::string& data) final {
|
||||
osmium::io::detail::reliable_write(m_fd, data.data(), data.size());
|
||||
}
|
||||
|
||||
void close() final {
|
||||
if (m_fd >= 0) {
|
||||
const int fd = m_fd;
|
||||
m_fd = -1;
|
||||
if (do_fsync()) {
|
||||
osmium::io::detail::reliable_fsync(fd);
|
||||
}
|
||||
osmium::io::detail::reliable_close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
}; // class NoCompressor
|
||||
|
||||
class NoDecompressor : public Decompressor {
|
||||
|
||||
int m_fd;
|
||||
const char *m_buffer;
|
||||
std::size_t m_buffer_size;
|
||||
std::size_t m_offset = 0;
|
||||
|
||||
public:
|
||||
|
||||
explicit NoDecompressor(int fd) :
|
||||
Decompressor(),
|
||||
m_fd(fd),
|
||||
m_buffer(nullptr),
|
||||
m_buffer_size(0) {
|
||||
}
|
||||
|
||||
NoDecompressor(const char* buffer, std::size_t size) :
|
||||
Decompressor(),
|
||||
m_fd(-1),
|
||||
m_buffer(buffer),
|
||||
m_buffer_size(size) {
|
||||
}
|
||||
|
||||
~NoDecompressor() noexcept final {
|
||||
try {
|
||||
close();
|
||||
} catch (...) {
|
||||
// Ignore any exceptions because destructor must not throw.
|
||||
}
|
||||
}
|
||||
|
||||
std::string read() final {
|
||||
std::string buffer;
|
||||
|
||||
if (m_buffer) {
|
||||
if (m_buffer_size != 0) {
|
||||
const std::size_t size = m_buffer_size;
|
||||
m_buffer_size = 0;
|
||||
buffer.append(m_buffer, size);
|
||||
}
|
||||
} else {
|
||||
buffer.resize(osmium::io::Decompressor::input_buffer_size);
|
||||
const auto nread = ::read(m_fd, const_cast<char*>(buffer.data()), osmium::io::Decompressor::input_buffer_size);
|
||||
if (nread < 0) {
|
||||
throw std::system_error{errno, std::system_category(), "Read failed"};
|
||||
}
|
||||
buffer.resize(std::string::size_type(nread));
|
||||
}
|
||||
|
||||
m_offset += buffer.size();
|
||||
set_offset(m_offset);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void close() final {
|
||||
if (m_fd >= 0) {
|
||||
const int fd = m_fd;
|
||||
m_fd = -1;
|
||||
osmium::io::detail::reliable_close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
}; // class NoDecompressor
|
||||
|
||||
namespace detail {
|
||||
|
||||
// we want the register_compression() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_no_compression = osmium::io::CompressionFactory::instance().register_compression(osmium::io::file_compression::none,
|
||||
[](int fd, fsync sync) { return new osmium::io::NoCompressor{fd, sync}; },
|
||||
[](int fd) { return new osmium::io::NoDecompressor{fd}; },
|
||||
[](const char* buffer, std::size_t size) { return new osmium::io::NoDecompressor{buffer, size}; }
|
||||
);
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_no_compression() noexcept {
|
||||
return registered_no_compression;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_COMPRESSION_HPP
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef OSMIUM_IO_DEBUG_OUTPUT_HPP
|
||||
#define OSMIUM_IO_DEBUG_OUTPUT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osmium/io/writer.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/detail/debug_output_format.hpp> // IWYU pragma: export
|
||||
|
||||
#endif // OSMIUM_IO_DEBUG_OUTPUT_HPP
|
||||
@@ -0,0 +1,603 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_DEBUG_OUTPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_DEBUG_OUTPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/crc.hpp>
|
||||
|
||||
#include <osmium/io/detail/output_format.hpp>
|
||||
#include <osmium/io/detail/queue_util.hpp>
|
||||
#include <osmium/io/detail/string_util.hpp>
|
||||
#include <osmium/io/file.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/memory/item_iterator.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/changeset.hpp>
|
||||
#include <osmium/osm/crc.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/thread/pool.hpp>
|
||||
#include <osmium/util/minmax.hpp>
|
||||
#include <osmium/visitor.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr const char* color_bold = "\x1b[1m";
|
||||
constexpr const char* color_black = "\x1b[30m";
|
||||
constexpr const char* color_gray = "\x1b[30;1m";
|
||||
constexpr const char* color_red = "\x1b[31m";
|
||||
constexpr const char* color_green = "\x1b[32m";
|
||||
constexpr const char* color_yellow = "\x1b[33m";
|
||||
constexpr const char* color_blue = "\x1b[34m";
|
||||
constexpr const char* color_magenta = "\x1b[35m";
|
||||
constexpr const char* color_cyan = "\x1b[36m";
|
||||
constexpr const char* color_white = "\x1b[37m";
|
||||
|
||||
constexpr const char* color_backg_red = "\x1b[41m";
|
||||
constexpr const char* color_backg_green = "\x1b[42m";
|
||||
|
||||
constexpr const char* color_reset = "\x1b[0m";
|
||||
|
||||
struct debug_output_options {
|
||||
|
||||
/// Should metadata of objects be added?
|
||||
bool add_metadata;
|
||||
|
||||
/// Output with ANSI colors?
|
||||
bool use_color;
|
||||
|
||||
/// Add CRC32 checksum to each object?
|
||||
bool add_crc32;
|
||||
|
||||
/// Write in form of a diff file?
|
||||
bool format_as_diff;
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes out one buffer with OSM data in Debug format.
|
||||
*/
|
||||
class DebugOutputBlock : public OutputBlock {
|
||||
|
||||
debug_output_options m_options;
|
||||
|
||||
const char* m_utf8_prefix = "";
|
||||
const char* m_utf8_suffix = "";
|
||||
|
||||
char m_diff_char = '\0';
|
||||
|
||||
void append_encoded_string(const char* data) {
|
||||
append_debug_encoded_string(*m_out, data, m_utf8_prefix, m_utf8_suffix);
|
||||
}
|
||||
|
||||
template <typename... TArgs>
|
||||
void output_formatted(const char* format, TArgs&&... args) {
|
||||
append_printf_formatted_string(*m_out, format, std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
void write_color(const char* color) {
|
||||
if (m_options.use_color) {
|
||||
*m_out += color;
|
||||
}
|
||||
}
|
||||
|
||||
void write_diff() {
|
||||
if (!m_diff_char) {
|
||||
return;
|
||||
}
|
||||
if (m_options.use_color) {
|
||||
if (m_diff_char == '-') {
|
||||
*m_out += color_backg_red;
|
||||
*m_out += color_white;
|
||||
*m_out += color_bold;
|
||||
*m_out += '-';
|
||||
*m_out += color_reset;
|
||||
return;
|
||||
} else if (m_diff_char == '+') {
|
||||
*m_out += color_backg_green;
|
||||
*m_out += color_white;
|
||||
*m_out += color_bold;
|
||||
*m_out += '+';
|
||||
*m_out += color_reset;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*m_out += m_diff_char;
|
||||
}
|
||||
|
||||
void write_string(const char* string) {
|
||||
*m_out += '"';
|
||||
write_color(color_blue);
|
||||
append_encoded_string(string);
|
||||
write_color(color_reset);
|
||||
*m_out += '"';
|
||||
}
|
||||
|
||||
void write_object_type(const char* object_type, bool visible = true) {
|
||||
write_diff();
|
||||
if (visible) {
|
||||
write_color(color_bold);
|
||||
} else {
|
||||
write_color(color_white);
|
||||
}
|
||||
*m_out += object_type;
|
||||
write_color(color_reset);
|
||||
*m_out += ' ';
|
||||
}
|
||||
|
||||
void write_fieldname(const char* name) {
|
||||
write_diff();
|
||||
*m_out += " ";
|
||||
write_color(color_cyan);
|
||||
*m_out += name;
|
||||
write_color(color_reset);
|
||||
*m_out += ": ";
|
||||
}
|
||||
|
||||
void write_comment_field(const char* name) {
|
||||
write_color(color_cyan);
|
||||
*m_out += name;
|
||||
write_color(color_reset);
|
||||
*m_out += ": ";
|
||||
}
|
||||
|
||||
void write_counter(int width, int n) {
|
||||
write_color(color_white);
|
||||
output_formatted(" %0*d: ", width, n++);
|
||||
write_color(color_reset);
|
||||
}
|
||||
|
||||
void write_error(const char* msg) {
|
||||
write_color(color_red);
|
||||
*m_out += msg;
|
||||
write_color(color_reset);
|
||||
}
|
||||
|
||||
void write_timestamp(const osmium::Timestamp& timestamp) {
|
||||
if (timestamp.valid()) {
|
||||
*m_out += timestamp.to_iso();
|
||||
*m_out += " (";
|
||||
output_int(timestamp.seconds_since_epoch());
|
||||
*m_out += ')';
|
||||
} else {
|
||||
write_error("NOT SET");
|
||||
}
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void write_meta(const osmium::OSMObject& object) {
|
||||
output_int(object.id());
|
||||
*m_out += '\n';
|
||||
if (m_options.add_metadata) {
|
||||
write_fieldname("version");
|
||||
*m_out += " ";
|
||||
output_int(object.version());
|
||||
if (object.visible()) {
|
||||
*m_out += " visible\n";
|
||||
} else {
|
||||
write_error(" deleted\n");
|
||||
}
|
||||
write_fieldname("changeset");
|
||||
output_int(object.changeset());
|
||||
*m_out += '\n';
|
||||
write_fieldname("timestamp");
|
||||
write_timestamp(object.timestamp());
|
||||
write_fieldname("user");
|
||||
*m_out += " ";
|
||||
output_int(object.uid());
|
||||
*m_out += ' ';
|
||||
write_string(object.user());
|
||||
*m_out += '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void write_tags(const osmium::TagList& tags, const char* padding="") {
|
||||
if (tags.empty()) {
|
||||
return;
|
||||
}
|
||||
write_fieldname("tags");
|
||||
*m_out += padding;
|
||||
*m_out += " ";
|
||||
output_int(tags.size());
|
||||
*m_out += '\n';
|
||||
|
||||
osmium::max_op<size_t> max;
|
||||
for (const auto& tag : tags) {
|
||||
max.update(std::strlen(tag.key()));
|
||||
}
|
||||
for (const auto& tag : tags) {
|
||||
write_diff();
|
||||
*m_out += " ";
|
||||
write_string(tag.key());
|
||||
auto spacing = max() - std::strlen(tag.key());
|
||||
while (spacing--) {
|
||||
*m_out += " ";
|
||||
}
|
||||
*m_out += " = ";
|
||||
write_string(tag.value());
|
||||
*m_out += '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void write_location(const osmium::Location& location) {
|
||||
write_fieldname("lon/lat");
|
||||
*m_out += " ";
|
||||
location.as_string_without_check(std::back_inserter(*m_out));
|
||||
if (!location.valid()) {
|
||||
write_error(" INVALID LOCATION!");
|
||||
}
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void write_box(const osmium::Box& box) {
|
||||
write_fieldname("box l/b/r/t");
|
||||
if (box.bottom_left().is_undefined() &&
|
||||
box.top_right().is_undefined()) {
|
||||
write_error("BOX NOT SET!\n");
|
||||
return;
|
||||
}
|
||||
const auto& bl = box.bottom_left();
|
||||
const auto& tr = box.top_right();
|
||||
bl.as_string_without_check(std::back_inserter(*m_out));
|
||||
*m_out += ' ';
|
||||
tr.as_string_without_check(std::back_inserter(*m_out));
|
||||
if (!box.valid()) {
|
||||
write_error(" INVALID BOX!");
|
||||
}
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_crc32(const T& object) {
|
||||
write_fieldname("crc32");
|
||||
osmium::CRC<boost::crc_32_type> crc32;
|
||||
crc32.update(object);
|
||||
output_formatted(" %x\n", crc32().checksum());
|
||||
}
|
||||
|
||||
void write_crc32(const osmium::Changeset& object) {
|
||||
write_fieldname("crc32");
|
||||
osmium::CRC<boost::crc_32_type> crc32;
|
||||
crc32.update(object);
|
||||
output_formatted(" %x\n", crc32().checksum());
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
DebugOutputBlock(osmium::memory::Buffer&& buffer, const debug_output_options& options) :
|
||||
OutputBlock(std::move(buffer)),
|
||||
m_options(options),
|
||||
m_utf8_prefix(options.use_color ? color_red : ""),
|
||||
m_utf8_suffix(options.use_color ? color_blue : "") {
|
||||
}
|
||||
|
||||
DebugOutputBlock(const DebugOutputBlock&) = default;
|
||||
DebugOutputBlock& operator=(const DebugOutputBlock&) = default;
|
||||
|
||||
DebugOutputBlock(DebugOutputBlock&&) = default;
|
||||
DebugOutputBlock& operator=(DebugOutputBlock&&) = default;
|
||||
|
||||
~DebugOutputBlock() noexcept = default;
|
||||
|
||||
std::string operator()() {
|
||||
osmium::apply(m_input_buffer->cbegin(), m_input_buffer->cend(), *this);
|
||||
|
||||
std::string out;
|
||||
using std::swap;
|
||||
swap(out, *m_out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void node(const osmium::Node& node) {
|
||||
m_diff_char = m_options.format_as_diff ? node.diff_as_char() : '\0';
|
||||
|
||||
write_object_type("node", node.visible());
|
||||
write_meta(node);
|
||||
|
||||
if (node.visible()) {
|
||||
write_location(node.location());
|
||||
}
|
||||
|
||||
write_tags(node.tags());
|
||||
|
||||
if (m_options.add_crc32) {
|
||||
write_crc32(node);
|
||||
}
|
||||
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void way(const osmium::Way& way) {
|
||||
m_diff_char = m_options.format_as_diff ? way.diff_as_char() : '\0';
|
||||
|
||||
write_object_type("way", way.visible());
|
||||
write_meta(way);
|
||||
write_tags(way.tags());
|
||||
|
||||
write_fieldname("nodes");
|
||||
|
||||
*m_out += " ";
|
||||
output_int(way.nodes().size());
|
||||
if (way.nodes().size() < 2) {
|
||||
write_error(" LESS THAN 2 NODES!\n");
|
||||
} else if (way.nodes().size() > 2000) {
|
||||
write_error(" MORE THAN 2000 NODES!\n");
|
||||
} else if (way.nodes().is_closed()) {
|
||||
*m_out += " (closed)\n";
|
||||
} else {
|
||||
*m_out += " (open)\n";
|
||||
}
|
||||
|
||||
const int width = int(std::log10(way.nodes().size())) + 1;
|
||||
int n = 0;
|
||||
for (const auto& node_ref : way.nodes()) {
|
||||
write_diff();
|
||||
write_counter(width, n++);
|
||||
output_formatted("%10" PRId64, node_ref.ref());
|
||||
if (node_ref.location().valid()) {
|
||||
*m_out += " (";
|
||||
node_ref.location().as_string(std::back_inserter(*m_out));
|
||||
*m_out += ')';
|
||||
}
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
if (m_options.add_crc32) {
|
||||
write_crc32(way);
|
||||
}
|
||||
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) {
|
||||
static const char* short_typename[] = { "node", "way ", "rel " };
|
||||
|
||||
m_diff_char = m_options.format_as_diff ? relation.diff_as_char() : '\0';
|
||||
|
||||
write_object_type("relation", relation.visible());
|
||||
write_meta(relation);
|
||||
write_tags(relation.tags());
|
||||
|
||||
write_fieldname("members");
|
||||
*m_out += " ";
|
||||
output_int(relation.members().size());
|
||||
*m_out += '\n';
|
||||
|
||||
const int width = int(std::log10(relation.members().size())) + 1;
|
||||
int n = 0;
|
||||
for (const auto& member : relation.members()) {
|
||||
write_diff();
|
||||
write_counter(width, n++);
|
||||
*m_out += short_typename[item_type_to_nwr_index(member.type())];
|
||||
output_formatted(" %10" PRId64 " ", member.ref());
|
||||
write_string(member.role());
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
if (m_options.add_crc32) {
|
||||
write_crc32(relation);
|
||||
}
|
||||
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void changeset(const osmium::Changeset& changeset) {
|
||||
write_object_type("changeset");
|
||||
output_int(changeset.id());
|
||||
*m_out += '\n';
|
||||
|
||||
write_fieldname("num changes");
|
||||
output_int(changeset.num_changes());
|
||||
if (changeset.num_changes() == 0) {
|
||||
write_error(" NO CHANGES!");
|
||||
}
|
||||
*m_out += '\n';
|
||||
|
||||
write_fieldname("created at");
|
||||
*m_out += ' ';
|
||||
write_timestamp(changeset.created_at());
|
||||
|
||||
write_fieldname("closed at");
|
||||
*m_out += " ";
|
||||
if (changeset.closed()) {
|
||||
write_timestamp(changeset.closed_at());
|
||||
} else {
|
||||
write_error("OPEN!\n");
|
||||
}
|
||||
|
||||
write_fieldname("user");
|
||||
*m_out += " ";
|
||||
output_int(changeset.uid());
|
||||
*m_out += ' ';
|
||||
write_string(changeset.user());
|
||||
*m_out += '\n';
|
||||
|
||||
write_box(changeset.bounds());
|
||||
write_tags(changeset.tags(), " ");
|
||||
|
||||
if (changeset.num_comments() > 0) {
|
||||
write_fieldname("comments");
|
||||
*m_out += " ";
|
||||
output_int(changeset.num_comments());
|
||||
*m_out += '\n';
|
||||
|
||||
const int width = int(std::log10(changeset.num_comments())) + 1;
|
||||
int n = 0;
|
||||
for (const auto& comment : changeset.discussion()) {
|
||||
write_counter(width, n++);
|
||||
|
||||
write_comment_field("date");
|
||||
write_timestamp(comment.date());
|
||||
output_formatted(" %*s", width, "");
|
||||
|
||||
write_comment_field("user");
|
||||
output_int(comment.uid());
|
||||
*m_out += ' ';
|
||||
write_string(comment.user());
|
||||
output_formatted("\n %*s", width, "");
|
||||
|
||||
write_comment_field("text");
|
||||
write_string(comment.text());
|
||||
*m_out += '\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (m_options.add_crc32) {
|
||||
write_crc32(changeset);
|
||||
}
|
||||
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
}; // class DebugOutputBlock
|
||||
|
||||
class DebugOutputFormat : public osmium::io::detail::OutputFormat {
|
||||
|
||||
debug_output_options m_options;
|
||||
|
||||
void write_fieldname(std::string& out, const char* name) {
|
||||
out += " ";
|
||||
if (m_options.use_color) {
|
||||
out += color_cyan;
|
||||
}
|
||||
out += name;
|
||||
if (m_options.use_color) {
|
||||
out += color_reset;
|
||||
}
|
||||
out += ": ";
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
DebugOutputFormat(osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) :
|
||||
OutputFormat(pool, output_queue),
|
||||
m_options() {
|
||||
m_options.add_metadata = file.is_not_false("add_metadata");
|
||||
m_options.use_color = file.is_true("color");
|
||||
m_options.add_crc32 = file.is_true("add_crc32");
|
||||
m_options.format_as_diff = file.is_true("diff");
|
||||
}
|
||||
|
||||
DebugOutputFormat(const DebugOutputFormat&) = delete;
|
||||
DebugOutputFormat& operator=(const DebugOutputFormat&) = delete;
|
||||
|
||||
~DebugOutputFormat() noexcept final = default;
|
||||
|
||||
void write_header(const osmium::io::Header& header) final {
|
||||
if (m_options.format_as_diff) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string out;
|
||||
|
||||
if (m_options.use_color) {
|
||||
out += color_bold;
|
||||
}
|
||||
out += "header\n";
|
||||
if (m_options.use_color) {
|
||||
out += color_reset;
|
||||
}
|
||||
|
||||
write_fieldname(out, "multiple object versions");
|
||||
out += header.has_multiple_object_versions() ? "yes" : "no";
|
||||
out += '\n';
|
||||
write_fieldname(out, "bounding boxes");
|
||||
out += '\n';
|
||||
for (const auto& box : header.boxes()) {
|
||||
out += " ";
|
||||
box.bottom_left().as_string(std::back_inserter(out));
|
||||
out += ' ';
|
||||
box.top_right().as_string(std::back_inserter(out));
|
||||
out += '\n';
|
||||
}
|
||||
write_fieldname(out, "options");
|
||||
out += '\n';
|
||||
for (const auto& opt : header) {
|
||||
out += " ";
|
||||
out += opt.first;
|
||||
out += " = ";
|
||||
out += opt.second;
|
||||
out += '\n';
|
||||
}
|
||||
out += "\n=============================================\n\n";
|
||||
|
||||
send_to_output_queue(std::move(out));
|
||||
}
|
||||
|
||||
void write_buffer(osmium::memory::Buffer&& buffer) final {
|
||||
m_output_queue.push(m_pool.submit(DebugOutputBlock{std::move(buffer), m_options}));
|
||||
}
|
||||
|
||||
}; // class DebugOutputFormat
|
||||
|
||||
// we want the register_output_format() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_debug_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::debug,
|
||||
[](osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) {
|
||||
return new osmium::io::detail::DebugOutputFormat(pool, file, output_queue);
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_debug_output() noexcept {
|
||||
return registered_debug_output;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_DEBUG_OUTPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,221 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_INPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_INPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/io/detail/queue_util.hpp>
|
||||
#include <osmium/io/error.hpp>
|
||||
#include <osmium/io/file.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm/entity_bits.hpp>
|
||||
#include <osmium/thread/pool.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct parser_arguments {
|
||||
osmium::thread::Pool& pool;
|
||||
future_string_queue_type& input_queue;
|
||||
future_buffer_queue_type& output_queue;
|
||||
std::promise<osmium::io::Header>& header_promise;
|
||||
osmium::osm_entity_bits::type read_which_entities;
|
||||
osmium::io::read_meta read_metadata;
|
||||
};
|
||||
|
||||
class Parser {
|
||||
|
||||
osmium::thread::Pool& m_pool;
|
||||
future_buffer_queue_type& m_output_queue;
|
||||
std::promise<osmium::io::Header>& m_header_promise;
|
||||
queue_wrapper<std::string> m_input_queue;
|
||||
osmium::osm_entity_bits::type m_read_which_entities;
|
||||
osmium::io::read_meta m_read_metadata;
|
||||
bool m_header_is_done;
|
||||
|
||||
protected:
|
||||
|
||||
osmium::thread::Pool& get_pool() {
|
||||
return m_pool;
|
||||
}
|
||||
|
||||
osmium::osm_entity_bits::type read_types() const noexcept {
|
||||
return m_read_which_entities;
|
||||
}
|
||||
|
||||
osmium::io::read_meta read_metadata() const noexcept {
|
||||
return m_read_metadata;
|
||||
}
|
||||
|
||||
bool header_is_done() const noexcept {
|
||||
return m_header_is_done;
|
||||
}
|
||||
|
||||
void set_header_value(const osmium::io::Header& header) {
|
||||
if (!m_header_is_done) {
|
||||
m_header_is_done = true;
|
||||
m_header_promise.set_value(header);
|
||||
}
|
||||
}
|
||||
|
||||
void set_header_exception(const std::exception_ptr& exception) {
|
||||
if (!m_header_is_done) {
|
||||
m_header_is_done = true;
|
||||
m_header_promise.set_exception(exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the buffer into a future and add it to the output queue.
|
||||
*/
|
||||
void send_to_output_queue(osmium::memory::Buffer&& buffer) {
|
||||
add_to_queue(m_output_queue, std::move(buffer));
|
||||
}
|
||||
|
||||
void send_to_output_queue(std::future<osmium::memory::Buffer>&& future) {
|
||||
m_output_queue.push(std::move(future));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit Parser(parser_arguments& args) :
|
||||
m_pool(args.pool),
|
||||
m_output_queue(args.output_queue),
|
||||
m_header_promise(args.header_promise),
|
||||
m_input_queue(args.input_queue),
|
||||
m_read_which_entities(args.read_which_entities),
|
||||
m_read_metadata(args.read_metadata),
|
||||
m_header_is_done(false) {
|
||||
}
|
||||
|
||||
Parser(const Parser&) = delete;
|
||||
Parser& operator=(const Parser&) = delete;
|
||||
|
||||
Parser(Parser&&) = delete;
|
||||
Parser& operator=(Parser&&) = delete;
|
||||
|
||||
virtual ~Parser() noexcept = default;
|
||||
|
||||
virtual void run() = 0;
|
||||
|
||||
std::string get_input() {
|
||||
return m_input_queue.pop();
|
||||
}
|
||||
|
||||
bool input_done() const {
|
||||
return m_input_queue.has_reached_end_of_data();
|
||||
}
|
||||
|
||||
void parse() {
|
||||
try {
|
||||
run();
|
||||
} catch (...) {
|
||||
std::exception_ptr exception = std::current_exception();
|
||||
set_header_exception(exception);
|
||||
add_to_queue(m_output_queue, std::move(exception));
|
||||
}
|
||||
|
||||
add_end_of_data_to_queue(m_output_queue);
|
||||
}
|
||||
|
||||
}; // class Parser
|
||||
|
||||
/**
|
||||
* This factory class is used to create objects that decode OSM
|
||||
* data written in a specified format.
|
||||
*
|
||||
* Do not use this class directly. Use the osmium::io::Reader
|
||||
* class instead.
|
||||
*/
|
||||
class ParserFactory {
|
||||
|
||||
public:
|
||||
|
||||
using create_parser_type = std::function<std::unique_ptr<Parser>(parser_arguments&)>;
|
||||
|
||||
private:
|
||||
|
||||
using map_type = std::map<osmium::io::file_format, create_parser_type>;
|
||||
|
||||
map_type m_callbacks;
|
||||
|
||||
ParserFactory() :
|
||||
m_callbacks() {
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static ParserFactory& instance() {
|
||||
static ParserFactory factory;
|
||||
return factory;
|
||||
}
|
||||
|
||||
bool register_parser(osmium::io::file_format format, create_parser_type&& create_function) {
|
||||
const auto result = m_callbacks.emplace(format, std::forward<create_parser_type>(create_function));
|
||||
return result.second;
|
||||
}
|
||||
|
||||
create_parser_type get_creator_function(const osmium::io::File& file) const {
|
||||
const auto it = m_callbacks.find(file.format());
|
||||
if (it == m_callbacks.end()) {
|
||||
throw unsupported_file_format_error{
|
||||
std::string{"Can not open file '"} +
|
||||
file.filename() +
|
||||
"' with type '" +
|
||||
as_string(file.format()) +
|
||||
"'. No support for reading this format in this program."};
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
}; // class ParserFactory
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_INPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,631 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_O5M_INPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_O5M_INPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <protozero/exception.hpp>
|
||||
#include <protozero/varint.hpp>
|
||||
|
||||
#include <osmium/builder/osm_object_builder.hpp>
|
||||
#include <osmium/io/detail/input_format.hpp>
|
||||
#include <osmium/io/detail/queue_util.hpp>
|
||||
#include <osmium/io/error.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/entity_bits.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/thread/util.hpp>
|
||||
#include <osmium/util/cast.hpp>
|
||||
#include <osmium/util/delta.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace builder {
|
||||
class Builder;
|
||||
} // namespace builder
|
||||
|
||||
/**
|
||||
* Exception thrown when the o5m deocder failed. The exception contains
|
||||
* (if available) information about the place where the error happened
|
||||
* and the type of error.
|
||||
*/
|
||||
struct o5m_error : public io_error {
|
||||
|
||||
explicit o5m_error(const char* what) :
|
||||
io_error(std::string{"o5m format error: "} + what) {
|
||||
}
|
||||
|
||||
}; // struct o5m_error
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Implementation of the o5m/o5c file formats according to the
|
||||
// description at http://wiki.openstreetmap.org/wiki/O5m .
|
||||
|
||||
class ReferenceTable {
|
||||
|
||||
// The following settings are from the o5m description:
|
||||
|
||||
// The maximum number of entries in this table.
|
||||
const uint64_t number_of_entries = 15000;
|
||||
|
||||
// The size of one entry in the table.
|
||||
const unsigned int entry_size = 256;
|
||||
|
||||
// The maximum length of a string in the table including
|
||||
// two \0 bytes.
|
||||
const unsigned int max_length = 250 + 2;
|
||||
|
||||
// The data is stored in this string. It is default constructed
|
||||
// and then resized on demand the first time something is added.
|
||||
// This is done because the ReferenceTable is in a O5mParser
|
||||
// object which will be copied from one thread to another. This
|
||||
// way the string is still small when it is copied.
|
||||
std::string m_table;
|
||||
|
||||
unsigned int current_entry = 0;
|
||||
|
||||
public:
|
||||
|
||||
void clear() {
|
||||
current_entry = 0;
|
||||
}
|
||||
|
||||
void add(const char* string, std::size_t size) {
|
||||
if (m_table.empty()) {
|
||||
m_table.resize(entry_size * number_of_entries);
|
||||
}
|
||||
if (size <= max_length) {
|
||||
std::copy_n(string, size, &m_table[current_entry * entry_size]);
|
||||
if (++current_entry == number_of_entries) {
|
||||
current_entry = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* get(uint64_t index) const {
|
||||
if (m_table.empty() || index == 0 || index > number_of_entries) {
|
||||
throw o5m_error{"reference to non-existing string in table"};
|
||||
}
|
||||
auto entry = (current_entry + number_of_entries - index) % number_of_entries;
|
||||
return &m_table[entry * entry_size];
|
||||
}
|
||||
|
||||
}; // class ReferenceTable
|
||||
|
||||
class O5mParser : public Parser {
|
||||
|
||||
static constexpr int buffer_size = 2 * 1000 * 1000;
|
||||
|
||||
osmium::io::Header m_header;
|
||||
|
||||
osmium::memory::Buffer m_buffer;
|
||||
|
||||
std::string m_input;
|
||||
|
||||
const char* m_data;
|
||||
const char* m_end;
|
||||
|
||||
ReferenceTable m_reference_table;
|
||||
|
||||
static int64_t zvarint(const char** data, const char* end) {
|
||||
return protozero::decode_zigzag64(protozero::decode_varint(data, end));
|
||||
}
|
||||
|
||||
bool ensure_bytes_available(std::size_t need_bytes) {
|
||||
if ((m_end - m_data) >= long(need_bytes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (input_done() && (m_input.size() < need_bytes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_input.erase(0, m_data - m_input.data());
|
||||
|
||||
while (m_input.size() < need_bytes) {
|
||||
const std::string data{get_input()};
|
||||
if (input_done()) {
|
||||
return false;
|
||||
}
|
||||
m_input.append(data);
|
||||
}
|
||||
|
||||
m_data = m_input.data();
|
||||
m_end = m_input.data() + m_input.size();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_header_magic() {
|
||||
static const unsigned char header_magic[] = { 0xff, 0xe0, 0x04, 'o', '5' };
|
||||
|
||||
if (std::strncmp(reinterpret_cast<const char*>(header_magic), m_data, sizeof(header_magic))) {
|
||||
throw o5m_error{"wrong header magic"};
|
||||
}
|
||||
|
||||
m_data += sizeof(header_magic);
|
||||
}
|
||||
|
||||
void check_file_type() {
|
||||
if (*m_data == 'm') { // o5m data file
|
||||
m_header.set_has_multiple_object_versions(false);
|
||||
} else if (*m_data == 'c') { // o5c change file
|
||||
m_header.set_has_multiple_object_versions(true);
|
||||
} else {
|
||||
throw o5m_error{"wrong header magic"};
|
||||
}
|
||||
|
||||
m_data++;
|
||||
}
|
||||
|
||||
void check_file_format_version() {
|
||||
if (*m_data != '2') {
|
||||
throw o5m_error{"wrong header magic"};
|
||||
}
|
||||
|
||||
m_data++;
|
||||
}
|
||||
|
||||
void decode_header() {
|
||||
if (! ensure_bytes_available(7)) { // overall length of header
|
||||
throw o5m_error{"file too short (incomplete header info)"};
|
||||
}
|
||||
|
||||
check_header_magic();
|
||||
check_file_type();
|
||||
check_file_format_version();
|
||||
}
|
||||
|
||||
void mark_header_as_done() {
|
||||
set_header_value(m_header);
|
||||
}
|
||||
|
||||
osmium::util::DeltaDecode<osmium::object_id_type> m_delta_id;
|
||||
|
||||
osmium::util::DeltaDecode<int64_t> m_delta_timestamp;
|
||||
osmium::util::DeltaDecode<osmium::changeset_id_type> m_delta_changeset;
|
||||
osmium::util::DeltaDecode<int64_t> m_delta_lon;
|
||||
osmium::util::DeltaDecode<int64_t> m_delta_lat;
|
||||
|
||||
osmium::util::DeltaDecode<osmium::object_id_type> m_delta_way_node_id;
|
||||
osmium::util::DeltaDecode<osmium::object_id_type> m_delta_member_ids[3];
|
||||
|
||||
void reset() {
|
||||
m_reference_table.clear();
|
||||
|
||||
m_delta_id.clear();
|
||||
m_delta_timestamp.clear();
|
||||
m_delta_changeset.clear();
|
||||
m_delta_lon.clear();
|
||||
m_delta_lat.clear();
|
||||
|
||||
m_delta_way_node_id.clear();
|
||||
m_delta_member_ids[0].clear();
|
||||
m_delta_member_ids[1].clear();
|
||||
m_delta_member_ids[2].clear();
|
||||
}
|
||||
|
||||
const char* decode_string(const char** dataptr, const char* const end) {
|
||||
if (**dataptr == 0x00) { // get inline string
|
||||
(*dataptr)++;
|
||||
if (*dataptr == end) {
|
||||
throw o5m_error{"string format error"};
|
||||
}
|
||||
return *dataptr;
|
||||
} else { // get from reference table
|
||||
auto index = protozero::decode_varint(dataptr, end);
|
||||
return m_reference_table.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<osmium::user_id_type, const char*> decode_user(const char** dataptr, const char* const end) {
|
||||
bool update_pointer = (**dataptr == 0x00);
|
||||
const char* data = decode_string(dataptr, end);
|
||||
const char* start = data;
|
||||
|
||||
auto uid = protozero::decode_varint(&data, end);
|
||||
|
||||
if (data == end) {
|
||||
throw o5m_error{"missing user name"};
|
||||
}
|
||||
|
||||
const char* user = ++data;
|
||||
|
||||
if (uid == 0 && update_pointer) {
|
||||
m_reference_table.add("\0\0", 2);
|
||||
*dataptr = data;
|
||||
return std::make_pair(0, "");
|
||||
}
|
||||
|
||||
while (*data++) {
|
||||
if (data == end) {
|
||||
throw o5m_error{"no null byte in user name"};
|
||||
}
|
||||
}
|
||||
|
||||
if (update_pointer) {
|
||||
m_reference_table.add(start, data - start);
|
||||
*dataptr = data;
|
||||
}
|
||||
|
||||
return std::make_pair(static_cast_with_assert<osmium::user_id_type>(uid), user);
|
||||
}
|
||||
|
||||
void decode_tags(osmium::builder::Builder& parent, const char** dataptr, const char* const end) {
|
||||
osmium::builder::TagListBuilder builder{parent};
|
||||
|
||||
while (*dataptr != end) {
|
||||
bool update_pointer = (**dataptr == 0x00);
|
||||
const char* data = decode_string(dataptr, end);
|
||||
const char* start = data;
|
||||
|
||||
while (*data++) {
|
||||
if (data == end) {
|
||||
throw o5m_error{"no null byte in tag key"};
|
||||
}
|
||||
}
|
||||
|
||||
const char* value = data;
|
||||
while (*data++) {
|
||||
if (data == end) {
|
||||
throw o5m_error{"no null byte in tag value"};
|
||||
}
|
||||
}
|
||||
|
||||
if (update_pointer) {
|
||||
m_reference_table.add(start, data - start);
|
||||
*dataptr = data;
|
||||
}
|
||||
|
||||
builder.add_tag(start, value);
|
||||
}
|
||||
}
|
||||
|
||||
const char* decode_info(osmium::OSMObject& object, const char** dataptr, const char* const end) {
|
||||
const char* user = "";
|
||||
|
||||
if (**dataptr == 0x00) { // no info section
|
||||
++*dataptr;
|
||||
} else { // has info section
|
||||
object.set_version(static_cast_with_assert<object_version_type>(protozero::decode_varint(dataptr, end)));
|
||||
auto timestamp = m_delta_timestamp.update(zvarint(dataptr, end));
|
||||
if (timestamp != 0) { // has timestamp
|
||||
object.set_timestamp(timestamp);
|
||||
object.set_changeset(m_delta_changeset.update(zvarint(dataptr, end)));
|
||||
if (*dataptr != end) {
|
||||
auto uid_user = decode_user(dataptr, end);
|
||||
object.set_uid(uid_user.first);
|
||||
user = uid_user.second;
|
||||
} else {
|
||||
object.set_uid(user_id_type(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
void decode_node(const char* data, const char* const end) {
|
||||
osmium::builder::NodeBuilder builder{m_buffer};
|
||||
|
||||
builder.set_id(m_delta_id.update(zvarint(&data, end)));
|
||||
|
||||
builder.set_user(decode_info(builder.object(), &data, end));
|
||||
|
||||
if (data == end) {
|
||||
// no location, object is deleted
|
||||
builder.set_visible(false);
|
||||
builder.set_location(osmium::Location{});
|
||||
} else {
|
||||
auto lon = m_delta_lon.update(zvarint(&data, end));
|
||||
auto lat = m_delta_lat.update(zvarint(&data, end));
|
||||
builder.set_location(osmium::Location{lon, lat});
|
||||
|
||||
if (data != end) {
|
||||
decode_tags(builder, &data, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decode_way(const char* data, const char* const end) {
|
||||
osmium::builder::WayBuilder builder{m_buffer};
|
||||
|
||||
builder.set_id(m_delta_id.update(zvarint(&data, end)));
|
||||
|
||||
builder.set_user(decode_info(builder.object(), &data, end));
|
||||
|
||||
if (data == end) {
|
||||
// no reference section, object is deleted
|
||||
builder.set_visible(false);
|
||||
} else {
|
||||
auto reference_section_length = protozero::decode_varint(&data, end);
|
||||
if (reference_section_length > 0) {
|
||||
const char* const end_refs = data + reference_section_length;
|
||||
if (end_refs > end) {
|
||||
throw o5m_error{"way nodes ref section too long"};
|
||||
}
|
||||
|
||||
osmium::builder::WayNodeListBuilder wn_builder{builder};
|
||||
|
||||
while (data < end_refs) {
|
||||
wn_builder.add_node_ref(m_delta_way_node_id.update(zvarint(&data, end)));
|
||||
}
|
||||
}
|
||||
|
||||
if (data != end) {
|
||||
decode_tags(builder, &data, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osmium::item_type decode_member_type(char c) {
|
||||
if (c < '0' || c > '2') {
|
||||
throw o5m_error{"unknown member type"};
|
||||
}
|
||||
return osmium::nwr_index_to_item_type(c - '0');
|
||||
}
|
||||
|
||||
std::pair<osmium::item_type, const char*> decode_role(const char** dataptr, const char* const end) {
|
||||
bool update_pointer = (**dataptr == 0x00);
|
||||
const char* data = decode_string(dataptr, end);
|
||||
const char* start = data;
|
||||
|
||||
auto member_type = decode_member_type(*data++);
|
||||
if (data == end) {
|
||||
throw o5m_error{"missing role"};
|
||||
}
|
||||
const char* role = data;
|
||||
|
||||
while (*data++) {
|
||||
if (data == end) {
|
||||
throw o5m_error{"no null byte in role"};
|
||||
}
|
||||
}
|
||||
|
||||
if (update_pointer) {
|
||||
m_reference_table.add(start, data - start);
|
||||
*dataptr = data;
|
||||
}
|
||||
|
||||
return std::make_pair(member_type, role);
|
||||
}
|
||||
|
||||
void decode_relation(const char* data, const char* const end) {
|
||||
osmium::builder::RelationBuilder builder{m_buffer};
|
||||
|
||||
builder.set_id(m_delta_id.update(zvarint(&data, end)));
|
||||
|
||||
builder.set_user(decode_info(builder.object(), &data, end));
|
||||
|
||||
if (data == end) {
|
||||
// no reference section, object is deleted
|
||||
builder.set_visible(false);
|
||||
} else {
|
||||
auto reference_section_length = protozero::decode_varint(&data, end);
|
||||
if (reference_section_length > 0) {
|
||||
const char* const end_refs = data + reference_section_length;
|
||||
if (end_refs > end) {
|
||||
throw o5m_error{"relation format error"};
|
||||
}
|
||||
|
||||
osmium::builder::RelationMemberListBuilder rml_builder{builder};
|
||||
|
||||
while (data < end_refs) {
|
||||
auto delta_id = zvarint(&data, end);
|
||||
if (data == end) {
|
||||
throw o5m_error{"relation member format error"};
|
||||
}
|
||||
auto type_role = decode_role(&data, end);
|
||||
auto i = osmium::item_type_to_nwr_index(type_role.first);
|
||||
auto ref = m_delta_member_ids[i].update(delta_id);
|
||||
rml_builder.add_member(type_role.first, ref, type_role.second);
|
||||
}
|
||||
}
|
||||
|
||||
if (data != end) {
|
||||
decode_tags(builder, &data, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decode_bbox(const char* data, const char* const end) {
|
||||
auto sw_lon = zvarint(&data, end);
|
||||
auto sw_lat = zvarint(&data, end);
|
||||
auto ne_lon = zvarint(&data, end);
|
||||
auto ne_lat = zvarint(&data, end);
|
||||
|
||||
m_header.add_box(osmium::Box{osmium::Location{sw_lon, sw_lat},
|
||||
osmium::Location{ne_lon, ne_lat}});
|
||||
}
|
||||
|
||||
void decode_timestamp(const char* data, const char* const end) {
|
||||
auto timestamp = osmium::Timestamp(zvarint(&data, end)).to_iso();
|
||||
m_header.set("o5m_timestamp", timestamp);
|
||||
m_header.set("timestamp", timestamp);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
osmium::memory::Buffer buffer(buffer_size);
|
||||
using std::swap;
|
||||
swap(m_buffer, buffer);
|
||||
send_to_output_queue(std::move(buffer));
|
||||
}
|
||||
|
||||
enum class dataset_type : unsigned char {
|
||||
node = 0x10,
|
||||
way = 0x11,
|
||||
relation = 0x12,
|
||||
bounding_box = 0xdb,
|
||||
timestamp = 0xdc,
|
||||
header = 0xe0,
|
||||
sync = 0xee,
|
||||
jump = 0xef,
|
||||
reset = 0xff
|
||||
};
|
||||
|
||||
void decode_data() {
|
||||
while (ensure_bytes_available(1)) {
|
||||
dataset_type ds_type = dataset_type(*m_data++);
|
||||
if (ds_type > dataset_type::jump) {
|
||||
if (ds_type == dataset_type::reset) {
|
||||
reset();
|
||||
}
|
||||
} else {
|
||||
ensure_bytes_available(protozero::max_varint_length);
|
||||
|
||||
uint64_t length = 0;
|
||||
try {
|
||||
length = protozero::decode_varint(&m_data, m_end);
|
||||
} catch (const protozero::end_of_buffer_exception&) {
|
||||
throw o5m_error{"premature end of file"};
|
||||
}
|
||||
|
||||
if (! ensure_bytes_available(length)) {
|
||||
throw o5m_error{"premature end of file"};
|
||||
}
|
||||
|
||||
switch (ds_type) {
|
||||
case dataset_type::node:
|
||||
mark_header_as_done();
|
||||
if (read_types() & osmium::osm_entity_bits::node) {
|
||||
decode_node(m_data, m_data + length);
|
||||
m_buffer.commit();
|
||||
}
|
||||
break;
|
||||
case dataset_type::way:
|
||||
mark_header_as_done();
|
||||
if (read_types() & osmium::osm_entity_bits::way) {
|
||||
decode_way(m_data, m_data + length);
|
||||
m_buffer.commit();
|
||||
}
|
||||
break;
|
||||
case dataset_type::relation:
|
||||
mark_header_as_done();
|
||||
if (read_types() & osmium::osm_entity_bits::relation) {
|
||||
decode_relation(m_data, m_data + length);
|
||||
m_buffer.commit();
|
||||
}
|
||||
break;
|
||||
case dataset_type::bounding_box:
|
||||
decode_bbox(m_data, m_data + length);
|
||||
break;
|
||||
case dataset_type::timestamp:
|
||||
decode_timestamp(m_data, m_data + length);
|
||||
break;
|
||||
default:
|
||||
// ignore unknown datasets
|
||||
break;
|
||||
}
|
||||
|
||||
if (read_types() == osmium::osm_entity_bits::nothing && header_is_done()) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_data += length;
|
||||
|
||||
if (m_buffer.committed() > buffer_size / 10 * 9) {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_buffer.committed()) {
|
||||
flush();
|
||||
}
|
||||
|
||||
mark_header_as_done();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit O5mParser(parser_arguments& args) :
|
||||
Parser(args),
|
||||
m_header(),
|
||||
m_buffer(buffer_size),
|
||||
m_input(),
|
||||
m_data(m_input.data()),
|
||||
m_end(m_data) {
|
||||
}
|
||||
|
||||
~O5mParser() noexcept final = default;
|
||||
|
||||
void run() final {
|
||||
osmium::thread::set_thread_name("_osmium_o5m_in");
|
||||
|
||||
decode_header();
|
||||
decode_data();
|
||||
}
|
||||
|
||||
}; // class O5mParser
|
||||
|
||||
// we want the register_parser() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_o5m_parser = ParserFactory::instance().register_parser(
|
||||
file_format::o5m,
|
||||
[](parser_arguments& args) {
|
||||
return std::unique_ptr<Parser>(new O5mParser{args});
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_o5m_parser() noexcept {
|
||||
return registered_o5m_parser;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_O5M_INPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,164 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_OPL_INPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_OPL_INPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/io/detail/input_format.hpp>
|
||||
#include <osmium/io/detail/opl_parser_functions.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/thread/util.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Feed data coming in blocks line by line to the OPL parser
|
||||
// function. This has been broken out of the OPLParser class
|
||||
// where it belongs into a standalone template function to be
|
||||
// better testable.
|
||||
template <typename T>
|
||||
void line_by_line(T& worker) {
|
||||
std::string rest;
|
||||
|
||||
while (!worker.input_done()) {
|
||||
std::string input{worker.get_input()};
|
||||
std::string::size_type ppos = 0;
|
||||
|
||||
if (!rest.empty()) {
|
||||
ppos = input.find_first_of("\n\r");
|
||||
if (ppos == std::string::npos) {
|
||||
rest.append(input);
|
||||
continue;
|
||||
}
|
||||
rest.append(input, 0, ppos);
|
||||
if (!rest.empty()) {
|
||||
worker.parse_line(rest.data());
|
||||
rest.clear();
|
||||
}
|
||||
++ppos;
|
||||
}
|
||||
|
||||
for (auto pos = input.find_first_of("\n\r", ppos);
|
||||
pos != std::string::npos;
|
||||
pos = input.find_first_of("\n\r", ppos)) {
|
||||
const char* data = &input[ppos];
|
||||
input[pos] = '\0';
|
||||
if (data[0] != '\0') {
|
||||
worker.parse_line(data);
|
||||
}
|
||||
ppos = pos + 1;
|
||||
if (ppos >= input.size()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
rest.assign(input, ppos, std::string::npos);
|
||||
}
|
||||
|
||||
if (!rest.empty()) {
|
||||
worker.parse_line(rest.data());
|
||||
}
|
||||
}
|
||||
|
||||
class OPLParser : public Parser {
|
||||
|
||||
osmium::memory::Buffer m_buffer{1024*1024};
|
||||
uint64_t m_line_count = 0;
|
||||
|
||||
void maybe_flush() {
|
||||
if (m_buffer.committed() > 800*1024) {
|
||||
osmium::memory::Buffer buffer{1024*1024};
|
||||
using std::swap;
|
||||
swap(m_buffer, buffer);
|
||||
send_to_output_queue(std::move(buffer));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit OPLParser(parser_arguments& args) :
|
||||
Parser(args) {
|
||||
set_header_value(osmium::io::Header{});
|
||||
}
|
||||
|
||||
~OPLParser() noexcept final = default;
|
||||
|
||||
void parse_line(const char* data) {
|
||||
if (opl_parse_line(m_line_count, data, m_buffer, read_types())) {
|
||||
maybe_flush();
|
||||
}
|
||||
++m_line_count;
|
||||
}
|
||||
|
||||
void run() final {
|
||||
osmium::thread::set_thread_name("_osmium_opl_in");
|
||||
|
||||
line_by_line(*this);
|
||||
|
||||
if (m_buffer.committed() > 0) {
|
||||
send_to_output_queue(std::move(m_buffer));
|
||||
}
|
||||
}
|
||||
|
||||
}; // class OPLParser
|
||||
|
||||
// we want the register_parser() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_opl_parser = ParserFactory::instance().register_parser(
|
||||
file_format::opl,
|
||||
[](parser_arguments& args) {
|
||||
return std::unique_ptr<Parser>(new OPLParser{args});
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_opl_parser() noexcept {
|
||||
return registered_opl_parser;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_OPL_INPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,324 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_OPL_OUTPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_OPL_OUTPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/io/detail/output_format.hpp>
|
||||
#include <osmium/io/detail/queue_util.hpp>
|
||||
#include <osmium/io/detail/string_util.hpp>
|
||||
#include <osmium/io/file.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/memory/collection.hpp>
|
||||
#include <osmium/memory/item_iterator.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/changeset.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/thread/pool.hpp>
|
||||
#include <osmium/visitor.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct opl_output_options {
|
||||
|
||||
/// Should metadata of objects be added?
|
||||
bool add_metadata;
|
||||
|
||||
/// Should node locations be added to ways?
|
||||
bool locations_on_ways;
|
||||
|
||||
/// Write in form of a diff file?
|
||||
bool format_as_diff;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes out one buffer with OSM data in OPL format.
|
||||
*/
|
||||
class OPLOutputBlock : public OutputBlock {
|
||||
|
||||
opl_output_options m_options;
|
||||
|
||||
void append_encoded_string(const char* data) {
|
||||
osmium::io::detail::append_utf8_encoded_string(*m_out, data);
|
||||
}
|
||||
|
||||
void write_field_int(char c, int64_t value) {
|
||||
*m_out += c;
|
||||
output_int(value);
|
||||
}
|
||||
|
||||
void write_field_timestamp(char c, const osmium::Timestamp& timestamp) {
|
||||
*m_out += c;
|
||||
*m_out += timestamp.to_iso();
|
||||
}
|
||||
|
||||
void write_tags(const osmium::TagList& tags) {
|
||||
*m_out += " T";
|
||||
|
||||
if (tags.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = tags.begin();
|
||||
append_encoded_string(it->key());
|
||||
*m_out += '=';
|
||||
append_encoded_string(it->value());
|
||||
|
||||
for (++it; it != tags.end(); ++it) {
|
||||
*m_out += ',';
|
||||
append_encoded_string(it->key());
|
||||
*m_out += '=';
|
||||
append_encoded_string(it->value());
|
||||
}
|
||||
}
|
||||
|
||||
void write_meta(const osmium::OSMObject& object) {
|
||||
output_int(object.id());
|
||||
if (m_options.add_metadata) {
|
||||
*m_out += ' ';
|
||||
write_field_int('v', object.version());
|
||||
*m_out += " d";
|
||||
*m_out += (object.visible() ? 'V' : 'D');
|
||||
*m_out += ' ';
|
||||
write_field_int('c', object.changeset());
|
||||
*m_out += ' ';
|
||||
write_field_timestamp('t', object.timestamp());
|
||||
*m_out += ' ';
|
||||
write_field_int('i', object.uid());
|
||||
*m_out += " u";
|
||||
append_encoded_string(object.user());
|
||||
}
|
||||
write_tags(object.tags());
|
||||
}
|
||||
|
||||
void write_location(const osmium::Location& location, const char x, const char y) {
|
||||
const bool not_undefined = !location.is_undefined();
|
||||
*m_out += ' ';
|
||||
*m_out += x;
|
||||
if (not_undefined) {
|
||||
osmium::detail::append_location_coordinate_to_string(std::back_inserter(*m_out), location.x());
|
||||
}
|
||||
*m_out += ' ';
|
||||
*m_out += y;
|
||||
if (not_undefined) {
|
||||
osmium::detail::append_location_coordinate_to_string(std::back_inserter(*m_out), location.y());
|
||||
}
|
||||
}
|
||||
|
||||
void write_diff(const osmium::OSMObject& object) {
|
||||
if (m_options.format_as_diff) {
|
||||
*m_out += object.diff_as_char();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
OPLOutputBlock(osmium::memory::Buffer&& buffer, const opl_output_options& options) :
|
||||
OutputBlock(std::move(buffer)),
|
||||
m_options(options) {
|
||||
}
|
||||
|
||||
OPLOutputBlock(const OPLOutputBlock&) = default;
|
||||
OPLOutputBlock& operator=(const OPLOutputBlock&) = default;
|
||||
|
||||
OPLOutputBlock(OPLOutputBlock&&) = default;
|
||||
OPLOutputBlock& operator=(OPLOutputBlock&&) = default;
|
||||
|
||||
~OPLOutputBlock() noexcept = default;
|
||||
|
||||
std::string operator()() {
|
||||
osmium::apply(m_input_buffer->cbegin(), m_input_buffer->cend(), *this);
|
||||
|
||||
std::string out;
|
||||
using std::swap;
|
||||
swap(out, *m_out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void node(const osmium::Node& node) {
|
||||
write_diff(node);
|
||||
*m_out += 'n';
|
||||
write_meta(node);
|
||||
write_location(node.location(), 'x', 'y');
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void write_field_ref(const osmium::NodeRef& node_ref) {
|
||||
write_field_int('n', node_ref.ref());
|
||||
*m_out += 'x';
|
||||
if (node_ref.location()) {
|
||||
node_ref.location().as_string(std::back_inserter(*m_out), 'y');
|
||||
} else {
|
||||
*m_out += 'y';
|
||||
}
|
||||
}
|
||||
|
||||
void way(const osmium::Way& way) {
|
||||
write_diff(way);
|
||||
*m_out += 'w';
|
||||
write_meta(way);
|
||||
|
||||
*m_out += " N";
|
||||
|
||||
if (!way.nodes().empty()) {
|
||||
auto it = way.nodes().begin();
|
||||
if (m_options.locations_on_ways) {
|
||||
write_field_ref(*it);
|
||||
for (++it; it != way.nodes().end(); ++it) {
|
||||
*m_out += ',';
|
||||
write_field_ref(*it);
|
||||
}
|
||||
} else {
|
||||
write_field_int('n', it->ref());
|
||||
for (++it; it != way.nodes().end(); ++it) {
|
||||
*m_out += ',';
|
||||
write_field_int('n', it->ref());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void relation_member(const osmium::RelationMember& member) {
|
||||
*m_out += item_type_to_char(member.type());
|
||||
output_int(member.ref());
|
||||
*m_out += '@';
|
||||
append_encoded_string(member.role());
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) {
|
||||
write_diff(relation);
|
||||
*m_out += 'r';
|
||||
write_meta(relation);
|
||||
|
||||
*m_out += " M";
|
||||
|
||||
if (!relation.members().empty()) {
|
||||
auto it = relation.members().begin();
|
||||
relation_member(*it);
|
||||
for (++it; it != relation.members().end(); ++it) {
|
||||
*m_out += ',';
|
||||
relation_member(*it);
|
||||
}
|
||||
}
|
||||
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void changeset(const osmium::Changeset& changeset) {
|
||||
write_field_int('c', changeset.id());
|
||||
*m_out += ' ';
|
||||
write_field_int('k', changeset.num_changes());
|
||||
*m_out += ' ';
|
||||
write_field_timestamp('s', changeset.created_at());
|
||||
*m_out += ' ';
|
||||
write_field_timestamp('e', changeset.closed_at());
|
||||
*m_out += ' ';
|
||||
write_field_int('d', changeset.num_comments());
|
||||
*m_out += ' ';
|
||||
write_field_int('i', changeset.uid());
|
||||
*m_out += " u";
|
||||
append_encoded_string(changeset.user());
|
||||
write_location(changeset.bounds().bottom_left(), 'x', 'y');
|
||||
write_location(changeset.bounds().top_right(), 'X', 'Y');
|
||||
write_tags(changeset.tags());
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
}; // class OPLOutputBlock
|
||||
|
||||
class OPLOutputFormat : public osmium::io::detail::OutputFormat {
|
||||
|
||||
opl_output_options m_options;
|
||||
|
||||
public:
|
||||
|
||||
OPLOutputFormat(osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) :
|
||||
OutputFormat(pool, output_queue),
|
||||
m_options() {
|
||||
m_options.add_metadata = file.is_not_false("add_metadata");
|
||||
m_options.locations_on_ways = file.is_true("locations_on_ways");
|
||||
m_options.format_as_diff = file.is_true("diff");
|
||||
}
|
||||
|
||||
OPLOutputFormat(const OPLOutputFormat&) = delete;
|
||||
OPLOutputFormat& operator=(const OPLOutputFormat&) = delete;
|
||||
|
||||
~OPLOutputFormat() noexcept final = default;
|
||||
|
||||
void write_buffer(osmium::memory::Buffer&& buffer) final {
|
||||
m_output_queue.push(m_pool.submit(OPLOutputBlock{std::move(buffer), m_options}));
|
||||
}
|
||||
|
||||
}; // class OPLOutputFormat
|
||||
|
||||
// we want the register_output_format() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_opl_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::opl,
|
||||
[](osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) {
|
||||
return new osmium::io::detail::OPLOutputFormat(pool, file, output_queue);
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_opl_output() noexcept {
|
||||
return registered_opl_output;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_OPL_OUTPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,734 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP
|
||||
#define OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <utf8.h>
|
||||
|
||||
#include <osmium/builder/osm_object_builder.hpp>
|
||||
#include <osmium/io/error.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/changeset.hpp>
|
||||
#include <osmium/osm/entity_bits.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace builder {
|
||||
class Builder;
|
||||
} // namespace builder
|
||||
|
||||
/**
|
||||
* Exception thrown when there was a problem with parsing the OPL format
|
||||
* of a file.
|
||||
*/
|
||||
struct opl_error : public io_error {
|
||||
|
||||
uint64_t line = 0;
|
||||
uint64_t column = 0;
|
||||
const char* data;
|
||||
std::string msg;
|
||||
|
||||
explicit opl_error(const std::string& what, const char* d = nullptr) :
|
||||
io_error(std::string("OPL error: ") + what),
|
||||
data(d),
|
||||
msg("OPL error: ") {
|
||||
msg.append(what);
|
||||
}
|
||||
|
||||
explicit opl_error(const char* what, const char* d = nullptr) :
|
||||
io_error(std::string{"OPL error: "} + what),
|
||||
data(d),
|
||||
msg("OPL error: ") {
|
||||
msg.append(what);
|
||||
}
|
||||
|
||||
void set_pos(uint64_t l, uint64_t col) {
|
||||
line = l;
|
||||
column = col;
|
||||
msg.append(" on line ");
|
||||
msg.append(std::to_string(line));
|
||||
msg.append(" column ");
|
||||
msg.append(std::to_string(column));
|
||||
}
|
||||
|
||||
const char* what() const noexcept override {
|
||||
return msg.c_str();
|
||||
}
|
||||
|
||||
}; // struct opl_error
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Consume consecutive space and tab characters. There must be
|
||||
* at least one.
|
||||
*/
|
||||
inline void opl_parse_space(const char** s) {
|
||||
if (**s != ' ' && **s != '\t') {
|
||||
throw opl_error{"expected space or tab character", *s};
|
||||
}
|
||||
do {
|
||||
++*s;
|
||||
} while (**s == ' ' || **s == '\t');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether s points to something else than the end of the
|
||||
* string or a space or tab.
|
||||
*/
|
||||
inline bool opl_non_empty(const char *s) {
|
||||
return *s != '\0' && *s != ' ' && *s != '\t';
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip to the next space or tab character or the end of the
|
||||
* string.
|
||||
*/
|
||||
inline const char* opl_skip_section(const char** s) noexcept {
|
||||
while (opl_non_empty(*s)) {
|
||||
++*s;
|
||||
}
|
||||
return *s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse OPL-escaped strings with hex code with a '%' at the end.
|
||||
* Appends resulting unicode character to the result string.
|
||||
*
|
||||
* Returns a pointer to next character that needs to be consumed.
|
||||
*/
|
||||
inline void opl_parse_escaped(const char** data, std::string& result) {
|
||||
const char* s = *data;
|
||||
uint32_t value = 0;
|
||||
const int max_length = sizeof(value) * 2 /* hex chars per byte */;
|
||||
int length = 0;
|
||||
while (++length <= max_length) {
|
||||
if (*s == '\0') {
|
||||
throw opl_error{"eol", s};
|
||||
}
|
||||
if (*s == '%') {
|
||||
++s;
|
||||
utf8::utf32to8(&value, &value + 1, std::back_inserter(result));
|
||||
*data = s;
|
||||
return;
|
||||
}
|
||||
value <<= 4;
|
||||
if (*s >= '0' && *s <= '9') {
|
||||
value += *s - '0';
|
||||
} else if (*s >= 'a' && *s <= 'f') {
|
||||
value += *s - 'a' + 10;
|
||||
} else if (*s >= 'A' && *s <= 'F') {
|
||||
value += *s - 'A' + 10;
|
||||
} else {
|
||||
throw opl_error{"not a hex char", s};
|
||||
}
|
||||
++s;
|
||||
}
|
||||
throw opl_error{"hex escape too long", s};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string up to end of string or next space, tab, comma, or
|
||||
* equal sign.
|
||||
*
|
||||
* Appends characters to the result string.
|
||||
*
|
||||
* Returns a pointer to next character that needs to be consumed.
|
||||
*/
|
||||
inline void opl_parse_string(const char** data, std::string& result) {
|
||||
const char* s = *data;
|
||||
while (true) {
|
||||
if (*s == '\0' || *s == ' ' || *s == '\t' || *s == ',' || *s == '=') {
|
||||
break;
|
||||
} else if (*s == '%') {
|
||||
++s;
|
||||
opl_parse_escaped(&s, result);
|
||||
} else {
|
||||
result += *s;
|
||||
++s;
|
||||
}
|
||||
}
|
||||
*data = s;
|
||||
}
|
||||
|
||||
// Arbitrary limit how long integers can get
|
||||
constexpr const int max_int_len = 16;
|
||||
|
||||
template <typename T>
|
||||
inline T opl_parse_int(const char** s) {
|
||||
if (**s == '\0') {
|
||||
throw opl_error{"expected integer", *s};
|
||||
}
|
||||
const bool negative = (**s == '-');
|
||||
if (negative) {
|
||||
++*s;
|
||||
}
|
||||
|
||||
int64_t value = 0;
|
||||
|
||||
int n = max_int_len;
|
||||
while (**s >= '0' && **s <= '9') {
|
||||
if (--n == 0) {
|
||||
throw opl_error{"integer too long", *s};
|
||||
}
|
||||
value *= 10;
|
||||
value += **s - '0';
|
||||
++*s;
|
||||
}
|
||||
|
||||
if (n == max_int_len) {
|
||||
throw opl_error{"expected integer", *s};
|
||||
}
|
||||
|
||||
if (negative) {
|
||||
value = -value;
|
||||
if (value < std::numeric_limits<T>::min()) {
|
||||
throw opl_error{"integer too long", *s};
|
||||
}
|
||||
} else {
|
||||
if (value > std::numeric_limits<T>::max()) {
|
||||
throw opl_error{"integer too long", *s};
|
||||
}
|
||||
}
|
||||
|
||||
return T(value);
|
||||
}
|
||||
|
||||
inline osmium::object_id_type opl_parse_id(const char** s) {
|
||||
return opl_parse_int<osmium::object_id_type>(s);
|
||||
}
|
||||
|
||||
inline osmium::changeset_id_type opl_parse_changeset_id(const char** s) {
|
||||
return opl_parse_int<osmium::changeset_id_type>(s);
|
||||
}
|
||||
|
||||
inline osmium::object_version_type opl_parse_version(const char** s) {
|
||||
return opl_parse_int<osmium::object_version_type>(s);
|
||||
}
|
||||
|
||||
inline bool opl_parse_visible(const char** data) {
|
||||
if (**data == 'V') {
|
||||
++*data;
|
||||
return true;
|
||||
}
|
||||
if (**data == 'D') {
|
||||
++*data;
|
||||
return false;
|
||||
}
|
||||
throw opl_error{"invalid visible flag", *data};
|
||||
}
|
||||
|
||||
inline osmium::user_id_type opl_parse_uid(const char** s) {
|
||||
return opl_parse_int<osmium::user_id_type>(s);
|
||||
}
|
||||
|
||||
inline osmium::Timestamp opl_parse_timestamp(const char** s) {
|
||||
try {
|
||||
if (**s == '\0' || **s == ' ' || **s == '\t') {
|
||||
return osmium::Timestamp{};
|
||||
}
|
||||
osmium::Timestamp timestamp{*s};
|
||||
*s += 20;
|
||||
return timestamp;
|
||||
} catch (const std::invalid_argument&) {
|
||||
throw opl_error{"can not parse timestamp", *s};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if data points to given character and consume it.
|
||||
* Throw error otherwise.
|
||||
*/
|
||||
inline void opl_parse_char(const char** data, char c) {
|
||||
if (**data == c) {
|
||||
++*data;
|
||||
return;
|
||||
}
|
||||
std::string msg{"expected '"};
|
||||
msg += c;
|
||||
msg += "'";
|
||||
throw opl_error{msg, *data};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a list of tags in the format 'key=value,key=value,...'
|
||||
*
|
||||
* Tags will be added to the buffer using a TagListBuilder.
|
||||
*/
|
||||
inline void opl_parse_tags(const char* s, osmium::memory::Buffer& buffer, osmium::builder::Builder* parent_builder = nullptr) {
|
||||
osmium::builder::TagListBuilder builder{buffer, parent_builder};
|
||||
std::string key;
|
||||
std::string value;
|
||||
while (true) {
|
||||
opl_parse_string(&s, key);
|
||||
opl_parse_char(&s, '=');
|
||||
opl_parse_string(&s, value);
|
||||
builder.add_tag(key, value);
|
||||
if (*s == ' ' || *s == '\t' || *s == '\0') {
|
||||
break;
|
||||
}
|
||||
opl_parse_char(&s, ',');
|
||||
key.clear();
|
||||
value.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a number of nodes in the format "nID,nID,nID..."
|
||||
*
|
||||
* Nodes will be added to the buffer using a WayNodeListBuilder.
|
||||
*/
|
||||
inline void opl_parse_way_nodes(const char* s, const char* e, osmium::memory::Buffer& buffer, osmium::builder::WayBuilder* parent_builder = nullptr) {
|
||||
if (s == e) {
|
||||
return;
|
||||
}
|
||||
osmium::builder::WayNodeListBuilder builder{buffer, parent_builder};
|
||||
|
||||
while (s < e) {
|
||||
opl_parse_char(&s, 'n');
|
||||
if (s == e) {
|
||||
throw opl_error{"expected integer", s};
|
||||
}
|
||||
|
||||
const osmium::object_id_type ref = opl_parse_id(&s);
|
||||
if (s == e) {
|
||||
builder.add_node_ref(ref);
|
||||
return;
|
||||
}
|
||||
|
||||
osmium::Location location;
|
||||
if (*s == 'x') {
|
||||
++s;
|
||||
location.set_lon_partial(&s);
|
||||
if (*s == 'y') {
|
||||
++s;
|
||||
location.set_lat_partial(&s);
|
||||
}
|
||||
}
|
||||
|
||||
builder.add_node_ref(ref, location);
|
||||
|
||||
if (s == e) {
|
||||
return;
|
||||
}
|
||||
|
||||
opl_parse_char(&s, ',');
|
||||
}
|
||||
}
|
||||
|
||||
inline void opl_parse_node(const char** data, osmium::memory::Buffer& buffer) {
|
||||
osmium::builder::NodeBuilder builder{buffer};
|
||||
|
||||
builder.set_id(opl_parse_id(data));
|
||||
|
||||
const char* tags_begin = nullptr;
|
||||
|
||||
std::string user;
|
||||
osmium::Location location;
|
||||
while (**data) {
|
||||
opl_parse_space(data);
|
||||
const char c = **data;
|
||||
if (c == '\0') {
|
||||
break;
|
||||
}
|
||||
++(*data);
|
||||
switch (c) {
|
||||
case 'v':
|
||||
builder.set_version(opl_parse_version(data));
|
||||
break;
|
||||
case 'd':
|
||||
builder.set_visible(opl_parse_visible(data));
|
||||
break;
|
||||
case 'c':
|
||||
builder.set_changeset(opl_parse_changeset_id(data));
|
||||
break;
|
||||
case 't':
|
||||
builder.set_timestamp(opl_parse_timestamp(data));
|
||||
break;
|
||||
case 'i':
|
||||
builder.set_uid(opl_parse_uid(data));
|
||||
break;
|
||||
case 'u':
|
||||
opl_parse_string(data, user);
|
||||
break;
|
||||
case 'T':
|
||||
if (opl_non_empty(*data)) {
|
||||
tags_begin = *data;
|
||||
opl_skip_section(data);
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
if (opl_non_empty(*data)) {
|
||||
location.set_lon_partial(data);
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (opl_non_empty(*data)) {
|
||||
location.set_lat_partial(data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
--(*data);
|
||||
throw opl_error{"unknown attribute", *data};
|
||||
}
|
||||
}
|
||||
|
||||
if (location.valid()) {
|
||||
builder.set_location(location);
|
||||
}
|
||||
|
||||
builder.set_user(user);
|
||||
|
||||
if (tags_begin) {
|
||||
opl_parse_tags(tags_begin, buffer, &builder);
|
||||
}
|
||||
}
|
||||
|
||||
inline void opl_parse_way(const char** data, osmium::memory::Buffer& buffer) {
|
||||
osmium::builder::WayBuilder builder{buffer};
|
||||
|
||||
builder.set_id(opl_parse_id(data));
|
||||
|
||||
const char* tags_begin = nullptr;
|
||||
|
||||
const char* nodes_begin = nullptr;
|
||||
const char* nodes_end = nullptr;
|
||||
|
||||
std::string user;
|
||||
while (**data) {
|
||||
opl_parse_space(data);
|
||||
const char c = **data;
|
||||
if (c == '\0') {
|
||||
break;
|
||||
}
|
||||
++(*data);
|
||||
switch (c) {
|
||||
case 'v':
|
||||
builder.set_version(opl_parse_version(data));
|
||||
break;
|
||||
case 'd':
|
||||
builder.set_visible(opl_parse_visible(data));
|
||||
break;
|
||||
case 'c':
|
||||
builder.set_changeset(opl_parse_changeset_id(data));
|
||||
break;
|
||||
case 't':
|
||||
builder.set_timestamp(opl_parse_timestamp(data));
|
||||
break;
|
||||
case 'i':
|
||||
builder.set_uid(opl_parse_uid(data));
|
||||
break;
|
||||
case 'u':
|
||||
opl_parse_string(data, user);
|
||||
break;
|
||||
case 'T':
|
||||
if (opl_non_empty(*data)) {
|
||||
tags_begin = *data;
|
||||
opl_skip_section(data);
|
||||
}
|
||||
break;
|
||||
case 'N':
|
||||
nodes_begin = *data;
|
||||
nodes_end = opl_skip_section(data);
|
||||
break;
|
||||
default:
|
||||
--(*data);
|
||||
throw opl_error{"unknown attribute", *data};
|
||||
}
|
||||
}
|
||||
|
||||
builder.set_user(user);
|
||||
|
||||
if (tags_begin) {
|
||||
opl_parse_tags(tags_begin, buffer, &builder);
|
||||
}
|
||||
|
||||
opl_parse_way_nodes(nodes_begin, nodes_end, buffer, &builder);
|
||||
}
|
||||
|
||||
inline void opl_parse_relation_members(const char* s, const char* e, osmium::memory::Buffer& buffer, osmium::builder::RelationBuilder* parent_builder = nullptr) {
|
||||
if (s == e) {
|
||||
return;
|
||||
}
|
||||
osmium::builder::RelationMemberListBuilder builder{buffer, parent_builder};
|
||||
|
||||
while (s < e) {
|
||||
osmium::item_type type = osmium::char_to_item_type(*s);
|
||||
if (type != osmium::item_type::node &&
|
||||
type != osmium::item_type::way &&
|
||||
type != osmium::item_type::relation) {
|
||||
throw opl_error{"unknown object type", s};
|
||||
}
|
||||
++s;
|
||||
|
||||
if (s == e) {
|
||||
throw opl_error{"expected integer", s};
|
||||
}
|
||||
osmium::object_id_type ref = opl_parse_id(&s);
|
||||
opl_parse_char(&s, '@');
|
||||
if (s == e) {
|
||||
builder.add_member(type, ref, "");
|
||||
return;
|
||||
}
|
||||
std::string role;
|
||||
opl_parse_string(&s, role);
|
||||
builder.add_member(type, ref, role);
|
||||
|
||||
if (s == e) {
|
||||
return;
|
||||
}
|
||||
opl_parse_char(&s, ',');
|
||||
}
|
||||
}
|
||||
|
||||
inline void opl_parse_relation(const char** data, osmium::memory::Buffer& buffer) {
|
||||
osmium::builder::RelationBuilder builder{buffer};
|
||||
|
||||
builder.set_id(opl_parse_id(data));
|
||||
|
||||
const char* tags_begin = nullptr;
|
||||
|
||||
const char* members_begin = nullptr;
|
||||
const char* members_end = nullptr;
|
||||
|
||||
std::string user;
|
||||
while (**data) {
|
||||
opl_parse_space(data);
|
||||
const char c = **data;
|
||||
if (c == '\0') {
|
||||
break;
|
||||
}
|
||||
++(*data);
|
||||
switch (c) {
|
||||
case 'v':
|
||||
builder.set_version(opl_parse_version(data));
|
||||
break;
|
||||
case 'd':
|
||||
builder.set_visible(opl_parse_visible(data));
|
||||
break;
|
||||
case 'c':
|
||||
builder.set_changeset(opl_parse_changeset_id(data));
|
||||
break;
|
||||
case 't':
|
||||
builder.set_timestamp(opl_parse_timestamp(data));
|
||||
break;
|
||||
case 'i':
|
||||
builder.set_uid(opl_parse_uid(data));
|
||||
break;
|
||||
case 'u':
|
||||
opl_parse_string(data, user);
|
||||
break;
|
||||
case 'T':
|
||||
if (opl_non_empty(*data)) {
|
||||
tags_begin = *data;
|
||||
opl_skip_section(data);
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
members_begin = *data;
|
||||
members_end = opl_skip_section(data);
|
||||
break;
|
||||
default:
|
||||
--(*data);
|
||||
throw opl_error{"unknown attribute", *data};
|
||||
}
|
||||
}
|
||||
|
||||
builder.set_user(user);
|
||||
|
||||
if (tags_begin) {
|
||||
opl_parse_tags(tags_begin, buffer, &builder);
|
||||
}
|
||||
|
||||
if (members_begin != members_end) {
|
||||
opl_parse_relation_members(members_begin, members_end, buffer, &builder);
|
||||
}
|
||||
}
|
||||
|
||||
inline void opl_parse_changeset(const char** data, osmium::memory::Buffer& buffer) {
|
||||
osmium::builder::ChangesetBuilder builder{buffer};
|
||||
|
||||
builder.set_id(opl_parse_changeset_id(data));
|
||||
|
||||
const char* tags_begin = nullptr;
|
||||
|
||||
osmium::Box box;
|
||||
std::string user;
|
||||
while (**data) {
|
||||
opl_parse_space(data);
|
||||
const char c = **data;
|
||||
if (c == '\0') {
|
||||
break;
|
||||
}
|
||||
++(*data);
|
||||
switch (c) {
|
||||
case 'k':
|
||||
builder.set_num_changes(opl_parse_int<osmium::num_changes_type>(data));
|
||||
break;
|
||||
case 's':
|
||||
builder.set_created_at(opl_parse_timestamp(data));
|
||||
break;
|
||||
case 'e':
|
||||
builder.set_closed_at(opl_parse_timestamp(data));
|
||||
break;
|
||||
case 'd':
|
||||
builder.set_num_comments(opl_parse_int<osmium::num_comments_type>(data));
|
||||
break;
|
||||
case 'i':
|
||||
builder.set_uid(opl_parse_uid(data));
|
||||
break;
|
||||
case 'u':
|
||||
opl_parse_string(data, user);
|
||||
break;
|
||||
case 'x':
|
||||
if (opl_non_empty(*data)) {
|
||||
box.bottom_left().set_lon_partial(data);
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (opl_non_empty(*data)) {
|
||||
box.bottom_left().set_lat_partial(data);
|
||||
}
|
||||
break;
|
||||
case 'X':
|
||||
if (opl_non_empty(*data)) {
|
||||
box.top_right().set_lon_partial(data);
|
||||
}
|
||||
break;
|
||||
case 'Y':
|
||||
if (opl_non_empty(*data)) {
|
||||
box.top_right().set_lat_partial(data);
|
||||
}
|
||||
break;
|
||||
case 'T':
|
||||
if (opl_non_empty(*data)) {
|
||||
tags_begin = *data;
|
||||
opl_skip_section(data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
--(*data);
|
||||
throw opl_error{"unknown attribute", *data};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
builder.set_bounds(box);
|
||||
builder.set_user(user);
|
||||
|
||||
if (tags_begin) {
|
||||
opl_parse_tags(tags_begin, buffer, &builder);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool opl_parse_line(uint64_t line_count,
|
||||
const char* data,
|
||||
osmium::memory::Buffer& buffer,
|
||||
osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all) {
|
||||
const char* start_of_line = data;
|
||||
try {
|
||||
switch (*data) {
|
||||
case '\0':
|
||||
// ignore empty lines
|
||||
break;
|
||||
case '#':
|
||||
// ignore lines starting with #
|
||||
break;
|
||||
case 'n':
|
||||
if (read_types & osmium::osm_entity_bits::node) {
|
||||
++data;
|
||||
opl_parse_node(&data, buffer);
|
||||
buffer.commit();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
if (read_types & osmium::osm_entity_bits::way) {
|
||||
++data;
|
||||
opl_parse_way(&data, buffer);
|
||||
buffer.commit();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (read_types & osmium::osm_entity_bits::relation) {
|
||||
++data;
|
||||
opl_parse_relation(&data, buffer);
|
||||
buffer.commit();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if (read_types & osmium::osm_entity_bits::changeset) {
|
||||
++data;
|
||||
opl_parse_changeset(&data, buffer);
|
||||
buffer.commit();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw opl_error{"unknown type", data};
|
||||
}
|
||||
} catch (opl_error& e) {
|
||||
e.set_pos(line_count, e.data ? e.data - start_of_line : 0);
|
||||
throw;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP
|
||||
@@ -0,0 +1,236 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_OUTPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_OUTPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/handler.hpp>
|
||||
#include <osmium/io/detail/queue_util.hpp>
|
||||
#include <osmium/io/error.hpp>
|
||||
#include <osmium/io/file.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/thread/pool.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
class Header;
|
||||
} // namespace io
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class OutputBlock : public osmium::handler::Handler {
|
||||
|
||||
protected:
|
||||
|
||||
std::shared_ptr<osmium::memory::Buffer> m_input_buffer;
|
||||
|
||||
std::shared_ptr<std::string> m_out;
|
||||
|
||||
explicit OutputBlock(osmium::memory::Buffer&& buffer) :
|
||||
m_input_buffer(std::make_shared<osmium::memory::Buffer>(std::move(buffer))),
|
||||
m_out(std::make_shared<std::string>()) {
|
||||
}
|
||||
|
||||
// Simple function to convert integer to string. This is much
|
||||
// faster than using sprintf, but could be further optimized.
|
||||
// See https://github.com/miloyip/itoa-benchmark .
|
||||
void output_int(int64_t value) {
|
||||
if (value < 0) {
|
||||
*m_out += '-';
|
||||
value = -value;
|
||||
}
|
||||
|
||||
char temp[20];
|
||||
char *t = temp;
|
||||
do {
|
||||
*t++ = char(value % 10) + '0';
|
||||
value /= 10;
|
||||
} while (value > 0);
|
||||
|
||||
const auto old_size = m_out->size();
|
||||
m_out->resize(old_size + (t - temp));
|
||||
char* data = &(*m_out)[old_size];
|
||||
do {
|
||||
*data++ += *--t;
|
||||
} while (t != temp);
|
||||
}
|
||||
|
||||
}; // class OutputBlock;
|
||||
|
||||
/**
|
||||
* Virtual base class for all classes writing OSM files in different
|
||||
* formats.
|
||||
*
|
||||
* Do not use this class or derived classes directly. Use the
|
||||
* osmium::io::Writer class instead.
|
||||
*/
|
||||
class OutputFormat {
|
||||
|
||||
protected:
|
||||
|
||||
osmium::thread::Pool& m_pool;
|
||||
future_string_queue_type& m_output_queue;
|
||||
|
||||
/**
|
||||
* Wrap the string into a future and add it to the output
|
||||
* queue.
|
||||
*/
|
||||
void send_to_output_queue(std::string&& data) {
|
||||
add_to_queue(m_output_queue, std::move(data));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
OutputFormat(osmium::thread::Pool& pool, future_string_queue_type& output_queue) :
|
||||
m_pool(pool),
|
||||
m_output_queue(output_queue) {
|
||||
}
|
||||
|
||||
OutputFormat(const OutputFormat&) = delete;
|
||||
OutputFormat(OutputFormat&&) = delete;
|
||||
|
||||
OutputFormat& operator=(const OutputFormat&) = delete;
|
||||
OutputFormat& operator=(OutputFormat&&) = delete;
|
||||
|
||||
virtual ~OutputFormat() noexcept = default;
|
||||
|
||||
virtual void write_header(const osmium::io::Header&) {
|
||||
}
|
||||
|
||||
virtual void write_buffer(osmium::memory::Buffer&&) = 0;
|
||||
|
||||
virtual void write_end() {
|
||||
}
|
||||
|
||||
}; // class OutputFormat
|
||||
|
||||
/**
|
||||
* This factory class is used to create objects that write OSM data
|
||||
* into a specified output format.
|
||||
*
|
||||
* Do not use this class directly. Instead use the osmium::io::Writer
|
||||
* class.
|
||||
*/
|
||||
class OutputFormatFactory {
|
||||
|
||||
public:
|
||||
|
||||
using create_output_type = std::function<osmium::io::detail::OutputFormat*(osmium::thread::Pool&, const osmium::io::File&, future_string_queue_type&)>;
|
||||
|
||||
private:
|
||||
|
||||
using map_type = std::map<osmium::io::file_format, create_output_type>;
|
||||
|
||||
map_type m_callbacks;
|
||||
|
||||
OutputFormatFactory() :
|
||||
m_callbacks() {
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static OutputFormatFactory& instance() {
|
||||
static OutputFormatFactory factory;
|
||||
return factory;
|
||||
}
|
||||
|
||||
bool register_output_format(osmium::io::file_format format, create_output_type create_function) {
|
||||
if (! m_callbacks.insert(map_type::value_type(format, create_function)).second) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<osmium::io::detail::OutputFormat> create_output(osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) {
|
||||
const auto it = m_callbacks.find(file.format());
|
||||
if (it != m_callbacks.end()) {
|
||||
return std::unique_ptr<osmium::io::detail::OutputFormat>((it->second)(pool, file, output_queue));
|
||||
}
|
||||
|
||||
throw unsupported_file_format_error{
|
||||
std::string{"Can not open file '"} +
|
||||
file.filename() +
|
||||
"' with type '" +
|
||||
as_string(file.format()) +
|
||||
"'. No support for writing this format in this program."};
|
||||
}
|
||||
|
||||
}; // class OutputFormatFactory
|
||||
|
||||
class BlackholeOutputFormat : public osmium::io::detail::OutputFormat {
|
||||
|
||||
public:
|
||||
|
||||
BlackholeOutputFormat(osmium::thread::Pool& pool, const osmium::io::File& /*file*/, future_string_queue_type& output_queue) :
|
||||
OutputFormat(pool, output_queue) {
|
||||
}
|
||||
|
||||
BlackholeOutputFormat(const BlackholeOutputFormat&) = delete;
|
||||
BlackholeOutputFormat& operator=(const BlackholeOutputFormat&) = delete;
|
||||
|
||||
~BlackholeOutputFormat() noexcept final = default;
|
||||
|
||||
void write_buffer(osmium::memory::Buffer&& /*buffer*/) final {
|
||||
}
|
||||
|
||||
}; // class BlackholeOutputFormat
|
||||
|
||||
// we want the register_output_format() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_blackhole_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::blackhole,
|
||||
[](osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) {
|
||||
return new osmium::io::detail::BlackholeOutputFormat(pool, file, output_queue);
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_blackhole_output() noexcept {
|
||||
return registered_blackhole_output;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_OUTPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,89 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_PBF_HPP
|
||||
#define OSMIUM_IO_DETAIL_PBF_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
// needed for htonl and ntohl
|
||||
#ifndef _WIN32
|
||||
# include <netinet/in.h>
|
||||
#else
|
||||
# include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#include <osmium/io/error.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* Exception thrown when there was a problem with parsing the PBF format of
|
||||
* a file.
|
||||
*/
|
||||
struct pbf_error : public io_error {
|
||||
|
||||
explicit pbf_error(const std::string& what) :
|
||||
io_error(std::string("PBF error: ") + what) {
|
||||
}
|
||||
|
||||
explicit pbf_error(const char* what) :
|
||||
io_error(std::string("PBF error: ") + what) {
|
||||
}
|
||||
|
||||
}; // struct pbf_error
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// the maximum size of a blob header in bytes
|
||||
const int max_blob_header_size = 64 * 1024; // 64 kB
|
||||
|
||||
// the maximum size of an uncompressed blob in bytes
|
||||
const uint64_t max_uncompressed_blob_size = 32 * 1024 * 1024; // 32 MB
|
||||
|
||||
// resolution for longitude/latitude used for conversion
|
||||
// between representation as double and as int
|
||||
const int64_t lonlat_resolution = 1000 * 1000 * 1000;
|
||||
|
||||
const int64_t resolution_convert = lonlat_resolution / osmium::detail::coordinate_precision;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_PBF_HPP
|
||||
@@ -0,0 +1,904 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_PBF_DECODER_HPP
|
||||
#define OSMIUM_IO_DETAIL_PBF_DECODER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <protozero/iterators.hpp>
|
||||
#include <protozero/pbf_message.hpp>
|
||||
#include <protozero/types.hpp>
|
||||
|
||||
#include <osmium/builder/osm_object_builder.hpp>
|
||||
#include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/detail/protobuf_tags.hpp>
|
||||
#include <osmium/io/detail/zlib.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/entity_bits.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/util/cast.hpp>
|
||||
#include <osmium/util/delta.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace builder {
|
||||
class Builder;
|
||||
} // namespace builder
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
using protozero::data_view;
|
||||
using osm_string_len_type = std::pair<const char*, osmium::string_size_type>;
|
||||
|
||||
class PBFPrimitiveBlockDecoder {
|
||||
|
||||
static constexpr const size_t initial_buffer_size = 2 * 1024 * 1024;
|
||||
|
||||
data_view m_data;
|
||||
std::vector<osm_string_len_type> m_stringtable;
|
||||
|
||||
int64_t m_lon_offset = 0;
|
||||
int64_t m_lat_offset = 0;
|
||||
int64_t m_date_factor = 1000;
|
||||
int32_t m_granularity = 100;
|
||||
|
||||
osmium::osm_entity_bits::type m_read_types;
|
||||
|
||||
osmium::memory::Buffer m_buffer { initial_buffer_size };
|
||||
|
||||
osmium::io::read_meta m_read_metadata;
|
||||
|
||||
void decode_stringtable(const data_view& data) {
|
||||
if (!m_stringtable.empty()) {
|
||||
throw osmium::pbf_error{"more than one stringtable in pbf file"};
|
||||
}
|
||||
|
||||
protozero::pbf_message<OSMFormat::StringTable> pbf_string_table{data};
|
||||
while (pbf_string_table.next(OSMFormat::StringTable::repeated_bytes_s)) {
|
||||
const auto str_view = pbf_string_table.get_view();
|
||||
if (str_view.size() > osmium::max_osm_string_length) {
|
||||
throw osmium::pbf_error{"overlong string in string table"};
|
||||
}
|
||||
m_stringtable.emplace_back(str_view.data(), osmium::string_size_type(str_view.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void decode_primitive_block_metadata() {
|
||||
protozero::pbf_message<OSMFormat::PrimitiveBlock> pbf_primitive_block{m_data};
|
||||
while (pbf_primitive_block.next()) {
|
||||
switch (pbf_primitive_block.tag()) {
|
||||
case OSMFormat::PrimitiveBlock::required_StringTable_stringtable:
|
||||
decode_stringtable(pbf_primitive_block.get_view());
|
||||
break;
|
||||
case OSMFormat::PrimitiveBlock::optional_int32_granularity:
|
||||
m_granularity = pbf_primitive_block.get_int32();
|
||||
break;
|
||||
case OSMFormat::PrimitiveBlock::optional_int32_date_granularity:
|
||||
m_date_factor = pbf_primitive_block.get_int32();
|
||||
break;
|
||||
case OSMFormat::PrimitiveBlock::optional_int64_lat_offset:
|
||||
m_lat_offset = pbf_primitive_block.get_int64();
|
||||
break;
|
||||
case OSMFormat::PrimitiveBlock::optional_int64_lon_offset:
|
||||
m_lon_offset = pbf_primitive_block.get_int64();
|
||||
break;
|
||||
default:
|
||||
pbf_primitive_block.skip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decode_primitive_block_data() {
|
||||
protozero::pbf_message<OSMFormat::PrimitiveBlock> pbf_primitive_block{m_data};
|
||||
while (pbf_primitive_block.next(OSMFormat::PrimitiveBlock::repeated_PrimitiveGroup_primitivegroup)) {
|
||||
protozero::pbf_message<OSMFormat::PrimitiveGroup> pbf_primitive_group = pbf_primitive_block.get_message();
|
||||
while (pbf_primitive_group.next()) {
|
||||
switch (pbf_primitive_group.tag()) {
|
||||
case OSMFormat::PrimitiveGroup::repeated_Node_nodes:
|
||||
if (m_read_types & osmium::osm_entity_bits::node) {
|
||||
decode_node(pbf_primitive_group.get_view());
|
||||
m_buffer.commit();
|
||||
} else {
|
||||
pbf_primitive_group.skip();
|
||||
}
|
||||
break;
|
||||
case OSMFormat::PrimitiveGroup::optional_DenseNodes_dense:
|
||||
if (m_read_types & osmium::osm_entity_bits::node) {
|
||||
if (m_read_metadata == osmium::io::read_meta::yes) {
|
||||
decode_dense_nodes(pbf_primitive_group.get_view());
|
||||
} else {
|
||||
decode_dense_nodes_without_metadata(pbf_primitive_group.get_view());
|
||||
}
|
||||
m_buffer.commit();
|
||||
} else {
|
||||
pbf_primitive_group.skip();
|
||||
}
|
||||
break;
|
||||
case OSMFormat::PrimitiveGroup::repeated_Way_ways:
|
||||
if (m_read_types & osmium::osm_entity_bits::way) {
|
||||
decode_way(pbf_primitive_group.get_view());
|
||||
m_buffer.commit();
|
||||
} else {
|
||||
pbf_primitive_group.skip();
|
||||
}
|
||||
break;
|
||||
case OSMFormat::PrimitiveGroup::repeated_Relation_relations:
|
||||
if (m_read_types & osmium::osm_entity_bits::relation) {
|
||||
decode_relation(pbf_primitive_group.get_view());
|
||||
m_buffer.commit();
|
||||
} else {
|
||||
pbf_primitive_group.skip();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pbf_primitive_group.skip();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osm_string_len_type decode_info(const data_view& data, osmium::OSMObject& object) {
|
||||
osm_string_len_type user{"", 0};
|
||||
|
||||
protozero::pbf_message<OSMFormat::Info> pbf_info{data};
|
||||
while (pbf_info.next()) {
|
||||
switch (pbf_info.tag()) {
|
||||
case OSMFormat::Info::optional_int32_version:
|
||||
{
|
||||
const auto version = pbf_info.get_int32();
|
||||
if (version < 0) {
|
||||
throw osmium::pbf_error{"object version must not be negative"};
|
||||
}
|
||||
object.set_version(static_cast_with_assert<object_version_type>(version));
|
||||
}
|
||||
break;
|
||||
case OSMFormat::Info::optional_int64_timestamp:
|
||||
object.set_timestamp(pbf_info.get_int64() * m_date_factor / 1000);
|
||||
break;
|
||||
case OSMFormat::Info::optional_int64_changeset:
|
||||
{
|
||||
const auto changeset_id = pbf_info.get_int64();
|
||||
if (changeset_id < 0) {
|
||||
throw osmium::pbf_error{"object changeset_id must not be negative"};
|
||||
}
|
||||
object.set_changeset(static_cast_with_assert<changeset_id_type>(changeset_id));
|
||||
}
|
||||
break;
|
||||
case OSMFormat::Info::optional_int32_uid:
|
||||
object.set_uid_from_signed(pbf_info.get_int32());
|
||||
break;
|
||||
case OSMFormat::Info::optional_uint32_user_sid:
|
||||
user = m_stringtable.at(pbf_info.get_uint32());
|
||||
break;
|
||||
case OSMFormat::Info::optional_bool_visible:
|
||||
object.set_visible(pbf_info.get_bool());
|
||||
break;
|
||||
default:
|
||||
pbf_info.skip();
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
using kv_type = protozero::iterator_range<protozero::pbf_reader::const_uint32_iterator>;
|
||||
|
||||
void build_tag_list(osmium::builder::Builder& parent, const kv_type& keys, const kv_type& vals) {
|
||||
if (!keys.empty()) {
|
||||
osmium::builder::TagListBuilder builder{parent};
|
||||
auto kit = keys.begin();
|
||||
auto vit = vals.begin();
|
||||
while (kit != keys.end()) {
|
||||
if (vit == vals.end()) {
|
||||
// this is against the spec, must have same number of elements
|
||||
throw osmium::pbf_error{"PBF format error"};
|
||||
}
|
||||
const auto& k = m_stringtable.at(*kit++);
|
||||
const auto& v = m_stringtable.at(*vit++);
|
||||
builder.add_tag(k.first, k.second, v.first, v.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t convert_pbf_coordinate(int64_t c) const {
|
||||
return int32_t((c * m_granularity + m_lon_offset) / resolution_convert);
|
||||
}
|
||||
|
||||
void decode_node(const data_view& data) {
|
||||
osmium::builder::NodeBuilder builder{m_buffer};
|
||||
osmium::Node& node = builder.object();
|
||||
|
||||
kv_type keys;
|
||||
kv_type vals;
|
||||
int64_t lon = std::numeric_limits<int64_t>::max();
|
||||
int64_t lat = std::numeric_limits<int64_t>::max();
|
||||
|
||||
osm_string_len_type user{"", 0};
|
||||
|
||||
protozero::pbf_message<OSMFormat::Node> pbf_node{data};
|
||||
while (pbf_node.next()) {
|
||||
switch (pbf_node.tag()) {
|
||||
case OSMFormat::Node::required_sint64_id:
|
||||
node.set_id(pbf_node.get_sint64());
|
||||
break;
|
||||
case OSMFormat::Node::packed_uint32_keys:
|
||||
keys = pbf_node.get_packed_uint32();
|
||||
break;
|
||||
case OSMFormat::Node::packed_uint32_vals:
|
||||
vals = pbf_node.get_packed_uint32();
|
||||
break;
|
||||
case OSMFormat::Node::optional_Info_info:
|
||||
if (m_read_metadata == osmium::io::read_meta::yes) {
|
||||
user = decode_info(pbf_node.get_view(), builder.object());
|
||||
} else {
|
||||
pbf_node.skip();
|
||||
}
|
||||
break;
|
||||
case OSMFormat::Node::required_sint64_lat:
|
||||
lat = pbf_node.get_sint64();
|
||||
break;
|
||||
case OSMFormat::Node::required_sint64_lon:
|
||||
lon = pbf_node.get_sint64();
|
||||
break;
|
||||
default:
|
||||
pbf_node.skip();
|
||||
}
|
||||
}
|
||||
|
||||
if (node.visible()) {
|
||||
if (lon == std::numeric_limits<int64_t>::max() ||
|
||||
lat == std::numeric_limits<int64_t>::max()) {
|
||||
throw osmium::pbf_error{"illegal coordinate format"};
|
||||
}
|
||||
node.set_location(osmium::Location{
|
||||
convert_pbf_coordinate(lon),
|
||||
convert_pbf_coordinate(lat)
|
||||
});
|
||||
}
|
||||
|
||||
builder.set_user(user.first, user.second);
|
||||
|
||||
build_tag_list(builder, keys, vals);
|
||||
}
|
||||
|
||||
void decode_way(const data_view& data) {
|
||||
osmium::builder::WayBuilder builder{m_buffer};
|
||||
|
||||
kv_type keys;
|
||||
kv_type vals;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> refs;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> lats;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> lons;
|
||||
|
||||
osm_string_len_type user{"", 0};
|
||||
|
||||
protozero::pbf_message<OSMFormat::Way> pbf_way{data};
|
||||
while (pbf_way.next()) {
|
||||
switch (pbf_way.tag()) {
|
||||
case OSMFormat::Way::required_int64_id:
|
||||
builder.object().set_id(pbf_way.get_int64());
|
||||
break;
|
||||
case OSMFormat::Way::packed_uint32_keys:
|
||||
keys = pbf_way.get_packed_uint32();
|
||||
break;
|
||||
case OSMFormat::Way::packed_uint32_vals:
|
||||
vals = pbf_way.get_packed_uint32();
|
||||
break;
|
||||
case OSMFormat::Way::optional_Info_info:
|
||||
if (m_read_metadata == osmium::io::read_meta::yes) {
|
||||
user = decode_info(pbf_way.get_view(), builder.object());
|
||||
} else {
|
||||
pbf_way.skip();
|
||||
}
|
||||
break;
|
||||
case OSMFormat::Way::packed_sint64_refs:
|
||||
refs = pbf_way.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::Way::packed_sint64_lat:
|
||||
lats = pbf_way.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::Way::packed_sint64_lon:
|
||||
lons = pbf_way.get_packed_sint64();
|
||||
break;
|
||||
default:
|
||||
pbf_way.skip();
|
||||
}
|
||||
}
|
||||
|
||||
builder.set_user(user.first, user.second);
|
||||
|
||||
if (!refs.empty()) {
|
||||
osmium::builder::WayNodeListBuilder wnl_builder{builder};
|
||||
osmium::util::DeltaDecode<int64_t> ref;
|
||||
if (lats.empty()) {
|
||||
for (const auto& ref_value : refs) {
|
||||
wnl_builder.add_node_ref(ref.update(ref_value));
|
||||
}
|
||||
} else {
|
||||
osmium::util::DeltaDecode<int64_t> lon;
|
||||
osmium::util::DeltaDecode<int64_t> lat;
|
||||
while (!refs.empty() && !lons.empty() && !lats.empty()) {
|
||||
wnl_builder.add_node_ref(
|
||||
ref.update(refs.front()),
|
||||
osmium::Location{convert_pbf_coordinate(lon.update(lons.front())),
|
||||
convert_pbf_coordinate(lat.update(lats.front()))}
|
||||
);
|
||||
refs.drop_front();
|
||||
lons.drop_front();
|
||||
lats.drop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
build_tag_list(builder, keys, vals);
|
||||
}
|
||||
|
||||
void decode_relation(const data_view& data) {
|
||||
osmium::builder::RelationBuilder builder{m_buffer};
|
||||
|
||||
kv_type keys;
|
||||
kv_type vals;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_int32_iterator> roles;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> refs;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_int32_iterator> types;
|
||||
|
||||
osm_string_len_type user{"", 0};
|
||||
|
||||
protozero::pbf_message<OSMFormat::Relation> pbf_relation{data};
|
||||
while (pbf_relation.next()) {
|
||||
switch (pbf_relation.tag()) {
|
||||
case OSMFormat::Relation::required_int64_id:
|
||||
builder.object().set_id(pbf_relation.get_int64());
|
||||
break;
|
||||
case OSMFormat::Relation::packed_uint32_keys:
|
||||
keys = pbf_relation.get_packed_uint32();
|
||||
break;
|
||||
case OSMFormat::Relation::packed_uint32_vals:
|
||||
vals = pbf_relation.get_packed_uint32();
|
||||
break;
|
||||
case OSMFormat::Relation::optional_Info_info:
|
||||
if (m_read_metadata == osmium::io::read_meta::yes) {
|
||||
user = decode_info(pbf_relation.get_view(), builder.object());
|
||||
} else {
|
||||
pbf_relation.skip();
|
||||
}
|
||||
break;
|
||||
case OSMFormat::Relation::packed_int32_roles_sid:
|
||||
roles = pbf_relation.get_packed_int32();
|
||||
break;
|
||||
case OSMFormat::Relation::packed_sint64_memids:
|
||||
refs = pbf_relation.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::Relation::packed_MemberType_types:
|
||||
types = pbf_relation.get_packed_enum();
|
||||
break;
|
||||
default:
|
||||
pbf_relation.skip();
|
||||
}
|
||||
}
|
||||
|
||||
builder.set_user(user.first, user.second);
|
||||
|
||||
if (!refs.empty()) {
|
||||
osmium::builder::RelationMemberListBuilder rml_builder{builder};
|
||||
osmium::util::DeltaDecode<int64_t> ref;
|
||||
while (!roles.empty() && !refs.empty() && !types.empty()) {
|
||||
const auto& r = m_stringtable.at(roles.front());
|
||||
const int type = types.front();
|
||||
if (type < 0 || type > 2) {
|
||||
throw osmium::pbf_error{"unknown relation member type"};
|
||||
}
|
||||
rml_builder.add_member(
|
||||
osmium::item_type(type + 1),
|
||||
ref.update(refs.front()),
|
||||
r.first,
|
||||
r.second
|
||||
);
|
||||
roles.drop_front();
|
||||
refs.drop_front();
|
||||
types.drop_front();
|
||||
}
|
||||
}
|
||||
|
||||
build_tag_list(builder, keys, vals);
|
||||
}
|
||||
|
||||
void build_tag_list_from_dense_nodes(osmium::builder::NodeBuilder& builder, protozero::pbf_reader::const_int32_iterator& it, protozero::pbf_reader::const_int32_iterator last) {
|
||||
osmium::builder::TagListBuilder tl_builder{builder};
|
||||
while (it != last && *it != 0) {
|
||||
const auto& k = m_stringtable.at(*it++);
|
||||
if (it == last) {
|
||||
throw osmium::pbf_error{"PBF format error"}; // this is against the spec, keys/vals must come in pairs
|
||||
}
|
||||
const auto& v = m_stringtable.at(*it++);
|
||||
tl_builder.add_tag(k.first, k.second, v.first, v.second);
|
||||
}
|
||||
|
||||
if (it != last) {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void decode_dense_nodes_without_metadata(const data_view& data) {
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> ids;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> lats;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> lons;
|
||||
|
||||
protozero::iterator_range<protozero::pbf_reader::const_int32_iterator> tags;
|
||||
|
||||
protozero::pbf_message<OSMFormat::DenseNodes> pbf_dense_nodes{data};
|
||||
while (pbf_dense_nodes.next()) {
|
||||
switch (pbf_dense_nodes.tag()) {
|
||||
case OSMFormat::DenseNodes::packed_sint64_id:
|
||||
ids = pbf_dense_nodes.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseNodes::packed_sint64_lat:
|
||||
lats = pbf_dense_nodes.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseNodes::packed_sint64_lon:
|
||||
lons = pbf_dense_nodes.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseNodes::packed_int32_keys_vals:
|
||||
tags = pbf_dense_nodes.get_packed_int32();
|
||||
break;
|
||||
default:
|
||||
pbf_dense_nodes.skip();
|
||||
}
|
||||
}
|
||||
|
||||
osmium::util::DeltaDecode<int64_t> dense_id;
|
||||
osmium::util::DeltaDecode<int64_t> dense_latitude;
|
||||
osmium::util::DeltaDecode<int64_t> dense_longitude;
|
||||
|
||||
auto tag_it = tags.begin();
|
||||
|
||||
while (!ids.empty()) {
|
||||
if (lons.empty() ||
|
||||
lats.empty()) {
|
||||
// this is against the spec, must have same number of elements
|
||||
throw osmium::pbf_error{"PBF format error"};
|
||||
}
|
||||
|
||||
osmium::builder::NodeBuilder builder{m_buffer};
|
||||
osmium::Node& node = builder.object();
|
||||
|
||||
node.set_id(dense_id.update(ids.front()));
|
||||
ids.drop_front();
|
||||
|
||||
const auto lon = dense_longitude.update(lons.front());
|
||||
lons.drop_front();
|
||||
const auto lat = dense_latitude.update(lats.front());
|
||||
lats.drop_front();
|
||||
builder.object().set_location(osmium::Location(
|
||||
convert_pbf_coordinate(lon),
|
||||
convert_pbf_coordinate(lat)
|
||||
));
|
||||
|
||||
if (tag_it != tags.end()) {
|
||||
build_tag_list_from_dense_nodes(builder, tag_it, tags.end());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void decode_dense_nodes(const data_view& data) {
|
||||
bool has_info = false;
|
||||
bool has_visibles = false;
|
||||
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> ids;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> lats;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> lons;
|
||||
|
||||
protozero::iterator_range<protozero::pbf_reader::const_int32_iterator> tags;
|
||||
|
||||
protozero::iterator_range<protozero::pbf_reader::const_int32_iterator> versions;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> timestamps;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint64_iterator> changesets;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint32_iterator> uids;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_sint32_iterator> user_sids;
|
||||
protozero::iterator_range<protozero::pbf_reader::const_int32_iterator> visibles;
|
||||
|
||||
protozero::pbf_message<OSMFormat::DenseNodes> pbf_dense_nodes{data};
|
||||
while (pbf_dense_nodes.next()) {
|
||||
switch (pbf_dense_nodes.tag()) {
|
||||
case OSMFormat::DenseNodes::packed_sint64_id:
|
||||
ids = pbf_dense_nodes.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseNodes::optional_DenseInfo_denseinfo:
|
||||
{
|
||||
has_info = true;
|
||||
protozero::pbf_message<OSMFormat::DenseInfo> pbf_dense_info{pbf_dense_nodes.get_message()};
|
||||
while (pbf_dense_info.next()) {
|
||||
switch (pbf_dense_info.tag()) {
|
||||
case OSMFormat::DenseInfo::packed_int32_version:
|
||||
versions = pbf_dense_info.get_packed_int32();
|
||||
break;
|
||||
case OSMFormat::DenseInfo::packed_sint64_timestamp:
|
||||
timestamps = pbf_dense_info.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseInfo::packed_sint64_changeset:
|
||||
changesets = pbf_dense_info.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseInfo::packed_sint32_uid:
|
||||
uids = pbf_dense_info.get_packed_sint32();
|
||||
break;
|
||||
case OSMFormat::DenseInfo::packed_sint32_user_sid:
|
||||
user_sids = pbf_dense_info.get_packed_sint32();
|
||||
break;
|
||||
case OSMFormat::DenseInfo::packed_bool_visible:
|
||||
has_visibles = true;
|
||||
visibles = pbf_dense_info.get_packed_bool();
|
||||
break;
|
||||
default:
|
||||
pbf_dense_info.skip();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OSMFormat::DenseNodes::packed_sint64_lat:
|
||||
lats = pbf_dense_nodes.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseNodes::packed_sint64_lon:
|
||||
lons = pbf_dense_nodes.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseNodes::packed_int32_keys_vals:
|
||||
tags = pbf_dense_nodes.get_packed_int32();
|
||||
break;
|
||||
default:
|
||||
pbf_dense_nodes.skip();
|
||||
}
|
||||
}
|
||||
|
||||
osmium::util::DeltaDecode<int64_t> dense_id;
|
||||
osmium::util::DeltaDecode<int64_t> dense_latitude;
|
||||
osmium::util::DeltaDecode<int64_t> dense_longitude;
|
||||
osmium::util::DeltaDecode<int64_t> dense_uid;
|
||||
osmium::util::DeltaDecode<int64_t> dense_user_sid;
|
||||
osmium::util::DeltaDecode<int64_t> dense_changeset;
|
||||
osmium::util::DeltaDecode<int64_t> dense_timestamp;
|
||||
|
||||
auto tag_it = tags.begin();
|
||||
|
||||
while (!ids.empty()) {
|
||||
if (lons.empty() ||
|
||||
lats.empty()) {
|
||||
// this is against the spec, must have same number of elements
|
||||
throw osmium::pbf_error{"PBF format error"};
|
||||
}
|
||||
|
||||
bool visible = true;
|
||||
|
||||
osmium::builder::NodeBuilder builder{m_buffer};
|
||||
osmium::Node& node = builder.object();
|
||||
|
||||
node.set_id(dense_id.update(ids.front()));
|
||||
ids.drop_front();
|
||||
|
||||
if (has_info) {
|
||||
if (versions.empty() ||
|
||||
changesets.empty() ||
|
||||
timestamps.empty() ||
|
||||
uids.empty() ||
|
||||
user_sids.empty()) {
|
||||
// this is against the spec, must have same number of elements
|
||||
throw osmium::pbf_error{"PBF format error"};
|
||||
}
|
||||
|
||||
const auto version = versions.front();
|
||||
versions.drop_front();
|
||||
if (version < 0) {
|
||||
throw osmium::pbf_error{"object version must not be negative"};
|
||||
}
|
||||
node.set_version(static_cast<osmium::object_version_type>(version));
|
||||
|
||||
const auto changeset_id = dense_changeset.update(changesets.front());
|
||||
changesets.drop_front();
|
||||
if (changeset_id < 0) {
|
||||
throw osmium::pbf_error{"object changeset_id must not be negative"};
|
||||
}
|
||||
node.set_changeset(static_cast<osmium::changeset_id_type>(changeset_id));
|
||||
|
||||
node.set_timestamp(dense_timestamp.update(timestamps.front()) * m_date_factor / 1000);
|
||||
timestamps.drop_front();
|
||||
node.set_uid_from_signed(static_cast<osmium::signed_user_id_type>(dense_uid.update(uids.front())));
|
||||
uids.drop_front();
|
||||
|
||||
if (has_visibles) {
|
||||
if (visibles.empty()) {
|
||||
// this is against the spec, must have same number of elements
|
||||
throw osmium::pbf_error{"PBF format error"};
|
||||
}
|
||||
visible = (visibles.front() != 0);
|
||||
visibles.drop_front();
|
||||
}
|
||||
node.set_visible(visible);
|
||||
|
||||
const auto& u = m_stringtable.at(dense_user_sid.update(user_sids.front()));
|
||||
user_sids.drop_front();
|
||||
builder.set_user(u.first, u.second);
|
||||
}
|
||||
|
||||
// even if the node isn't visible, there's still a record
|
||||
// of its lat/lon in the dense arrays.
|
||||
const auto lon = dense_longitude.update(lons.front());
|
||||
lons.drop_front();
|
||||
const auto lat = dense_latitude.update(lats.front());
|
||||
lats.drop_front();
|
||||
if (visible) {
|
||||
builder.object().set_location(osmium::Location{
|
||||
convert_pbf_coordinate(lon),
|
||||
convert_pbf_coordinate(lat)
|
||||
});
|
||||
}
|
||||
|
||||
if (tag_it != tags.end()) {
|
||||
build_tag_list_from_dense_nodes(builder, tag_it, tags.end());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
PBFPrimitiveBlockDecoder(const data_view& data, osmium::osm_entity_bits::type read_types, osmium::io::read_meta read_metadata) :
|
||||
m_data(data),
|
||||
m_read_types(read_types),
|
||||
m_read_metadata(read_metadata) {
|
||||
}
|
||||
|
||||
PBFPrimitiveBlockDecoder(const PBFPrimitiveBlockDecoder&) = delete;
|
||||
PBFPrimitiveBlockDecoder& operator=(const PBFPrimitiveBlockDecoder&) = delete;
|
||||
|
||||
PBFPrimitiveBlockDecoder(PBFPrimitiveBlockDecoder&&) = delete;
|
||||
PBFPrimitiveBlockDecoder& operator=(PBFPrimitiveBlockDecoder&&) = delete;
|
||||
|
||||
~PBFPrimitiveBlockDecoder() noexcept = default;
|
||||
|
||||
osmium::memory::Buffer operator()() {
|
||||
try {
|
||||
decode_primitive_block_metadata();
|
||||
decode_primitive_block_data();
|
||||
} catch (const std::out_of_range&) {
|
||||
throw osmium::pbf_error{"string id out of range"};
|
||||
}
|
||||
|
||||
return std::move(m_buffer);
|
||||
}
|
||||
|
||||
}; // class PBFPrimitiveBlockDecoder
|
||||
|
||||
inline data_view decode_blob(const std::string& blob_data, std::string& output) {
|
||||
int32_t raw_size = 0;
|
||||
protozero::data_view zlib_data;
|
||||
|
||||
protozero::pbf_message<FileFormat::Blob> pbf_blob{blob_data};
|
||||
while (pbf_blob.next()) {
|
||||
switch (pbf_blob.tag()) {
|
||||
case FileFormat::Blob::optional_bytes_raw:
|
||||
{
|
||||
const auto data_len = pbf_blob.get_view();
|
||||
if (data_len.size() > max_uncompressed_blob_size) {
|
||||
throw osmium::pbf_error{"illegal blob size"};
|
||||
}
|
||||
return data_len;
|
||||
}
|
||||
case FileFormat::Blob::optional_int32_raw_size:
|
||||
raw_size = pbf_blob.get_int32();
|
||||
if (raw_size <= 0 || uint32_t(raw_size) > max_uncompressed_blob_size) {
|
||||
throw osmium::pbf_error{"illegal blob size"};
|
||||
}
|
||||
break;
|
||||
case FileFormat::Blob::optional_bytes_zlib_data:
|
||||
zlib_data = pbf_blob.get_view();
|
||||
break;
|
||||
case FileFormat::Blob::optional_bytes_lzma_data:
|
||||
throw osmium::pbf_error{"lzma blobs not implemented"};
|
||||
default:
|
||||
throw osmium::pbf_error{"unknown compression"};
|
||||
}
|
||||
}
|
||||
|
||||
if (zlib_data.size() != 0 && raw_size != 0) {
|
||||
return osmium::io::detail::zlib_uncompress_string(
|
||||
zlib_data.data(),
|
||||
static_cast<unsigned long>(zlib_data.size()),
|
||||
static_cast<unsigned long>(raw_size),
|
||||
output
|
||||
);
|
||||
}
|
||||
|
||||
throw osmium::pbf_error{"blob contains no data"};
|
||||
}
|
||||
|
||||
inline osmium::Box decode_header_bbox(const data_view& data) {
|
||||
int64_t left = std::numeric_limits<int64_t>::max();
|
||||
int64_t right = std::numeric_limits<int64_t>::max();
|
||||
int64_t top = std::numeric_limits<int64_t>::max();
|
||||
int64_t bottom = std::numeric_limits<int64_t>::max();
|
||||
|
||||
protozero::pbf_message<OSMFormat::HeaderBBox> pbf_header_bbox{data};
|
||||
while (pbf_header_bbox.next()) {
|
||||
switch (pbf_header_bbox.tag()) {
|
||||
case OSMFormat::HeaderBBox::required_sint64_left:
|
||||
left = pbf_header_bbox.get_sint64();
|
||||
break;
|
||||
case OSMFormat::HeaderBBox::required_sint64_right:
|
||||
right = pbf_header_bbox.get_sint64();
|
||||
break;
|
||||
case OSMFormat::HeaderBBox::required_sint64_top:
|
||||
top = pbf_header_bbox.get_sint64();
|
||||
break;
|
||||
case OSMFormat::HeaderBBox::required_sint64_bottom:
|
||||
bottom = pbf_header_bbox.get_sint64();
|
||||
break;
|
||||
default:
|
||||
pbf_header_bbox.skip();
|
||||
}
|
||||
}
|
||||
|
||||
if (left == std::numeric_limits<int64_t>::max() ||
|
||||
right == std::numeric_limits<int64_t>::max() ||
|
||||
top == std::numeric_limits<int64_t>::max() ||
|
||||
bottom == std::numeric_limits<int64_t>::max()) {
|
||||
throw osmium::pbf_error{"invalid bbox"};
|
||||
}
|
||||
|
||||
osmium::Box box;
|
||||
box.extend(osmium::Location(left / resolution_convert, bottom / resolution_convert));
|
||||
box.extend(osmium::Location(right / resolution_convert, top / resolution_convert));
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
inline osmium::io::Header decode_header_block(const data_view& data) {
|
||||
osmium::io::Header header;
|
||||
int i = 0;
|
||||
|
||||
protozero::pbf_message<OSMFormat::HeaderBlock> pbf_header_block{data};
|
||||
while (pbf_header_block.next()) {
|
||||
switch (pbf_header_block.tag()) {
|
||||
case OSMFormat::HeaderBlock::optional_HeaderBBox_bbox:
|
||||
header.add_box(decode_header_bbox(pbf_header_block.get_view()));
|
||||
break;
|
||||
case OSMFormat::HeaderBlock::repeated_string_required_features:
|
||||
{
|
||||
auto feature = pbf_header_block.get_view();
|
||||
if (!std::strncmp("OsmSchema-V0.6", feature.data(), feature.size())) {
|
||||
// intentionally left blank
|
||||
} else if (!std::strncmp("DenseNodes", feature.data(), feature.size())) {
|
||||
header.set("pbf_dense_nodes", true);
|
||||
} else if (!std::strncmp("HistoricalInformation", feature.data(), feature.size())) {
|
||||
header.set_has_multiple_object_versions(true);
|
||||
} else {
|
||||
std::string msg{"required feature not supported: "};
|
||||
msg.append(feature.data(), feature.size());
|
||||
throw osmium::pbf_error{msg};
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OSMFormat::HeaderBlock::repeated_string_optional_features:
|
||||
header.set("pbf_optional_feature_" + std::to_string(i++), pbf_header_block.get_string());
|
||||
break;
|
||||
case OSMFormat::HeaderBlock::optional_string_writingprogram:
|
||||
header.set("generator", pbf_header_block.get_string());
|
||||
break;
|
||||
case OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp:
|
||||
{
|
||||
const auto timestamp = osmium::Timestamp{pbf_header_block.get_int64()}.to_iso();
|
||||
header.set("osmosis_replication_timestamp", timestamp);
|
||||
header.set("timestamp", timestamp);
|
||||
}
|
||||
break;
|
||||
case OSMFormat::HeaderBlock::optional_int64_osmosis_replication_sequence_number:
|
||||
header.set("osmosis_replication_sequence_number", std::to_string(pbf_header_block.get_int64()));
|
||||
break;
|
||||
case OSMFormat::HeaderBlock::optional_string_osmosis_replication_base_url:
|
||||
header.set("osmosis_replication_base_url", pbf_header_block.get_string());
|
||||
break;
|
||||
default:
|
||||
pbf_header_block.skip();
|
||||
}
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode HeaderBlock.
|
||||
*
|
||||
* @param header_block_data Input data
|
||||
* @returns Header object
|
||||
* @throws osmium::pbf_error If there was a parsing error
|
||||
*/
|
||||
inline osmium::io::Header decode_header(const std::string& header_block_data) {
|
||||
std::string output;
|
||||
|
||||
return decode_header_block(decode_blob(header_block_data, output));
|
||||
}
|
||||
|
||||
class PBFDataBlobDecoder {
|
||||
|
||||
std::shared_ptr<std::string> m_input_buffer;
|
||||
osmium::osm_entity_bits::type m_read_types;
|
||||
osmium::io::read_meta m_read_metadata;
|
||||
|
||||
public:
|
||||
|
||||
PBFDataBlobDecoder(std::string&& input_buffer, osmium::osm_entity_bits::type read_types, osmium::io::read_meta read_metadata) :
|
||||
m_input_buffer(std::make_shared<std::string>(std::move(input_buffer))),
|
||||
m_read_types(read_types),
|
||||
m_read_metadata(read_metadata) {
|
||||
}
|
||||
|
||||
PBFDataBlobDecoder(const PBFDataBlobDecoder&) = default;
|
||||
PBFDataBlobDecoder& operator=(const PBFDataBlobDecoder&) = default;
|
||||
|
||||
PBFDataBlobDecoder(PBFDataBlobDecoder&&) = default;
|
||||
PBFDataBlobDecoder& operator=(PBFDataBlobDecoder&&) = default;
|
||||
|
||||
~PBFDataBlobDecoder() noexcept = default;
|
||||
|
||||
osmium::memory::Buffer operator()() {
|
||||
std::string output;
|
||||
PBFPrimitiveBlockDecoder decoder{decode_blob(*m_input_buffer, output), m_read_types, m_read_metadata};
|
||||
return decoder();
|
||||
}
|
||||
|
||||
}; // class PBFDataBlobDecoder
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_PBF_DECODER_HPP
|
||||
@@ -0,0 +1,230 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_PBF_INPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_PBF_INPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <protozero/pbf_message.hpp>
|
||||
#include <protozero/types.hpp>
|
||||
|
||||
#include <osmium/io/detail/input_format.hpp>
|
||||
#include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/detail/pbf_decoder.hpp>
|
||||
#include <osmium/io/detail/protobuf_tags.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/osm/entity_bits.hpp>
|
||||
#include <osmium/thread/pool.hpp>
|
||||
#include <osmium/thread/util.hpp>
|
||||
#include <osmium/util/config.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class PBFParser : public Parser {
|
||||
|
||||
std::string m_input_buffer;
|
||||
|
||||
/**
|
||||
* Read the given number of bytes from the input queue.
|
||||
*
|
||||
* @param size Number of bytes to read
|
||||
* @returns String with the data
|
||||
* @throws osmium::pbf_error If size bytes can't be read
|
||||
*/
|
||||
std::string read_from_input_queue(size_t size) {
|
||||
while (m_input_buffer.size() < size) {
|
||||
const std::string new_data{get_input()};
|
||||
if (input_done()) {
|
||||
throw osmium::pbf_error{"truncated data (EOF encountered)"};
|
||||
}
|
||||
m_input_buffer += new_data;
|
||||
}
|
||||
|
||||
std::string output{m_input_buffer.substr(size)};
|
||||
m_input_buffer.resize(size);
|
||||
|
||||
using std::swap;
|
||||
swap(output, m_input_buffer);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read 4 bytes in network byte order from file. They contain
|
||||
* the length of the following BlobHeader.
|
||||
*/
|
||||
uint32_t read_blob_header_size_from_file() {
|
||||
uint32_t size_in_network_byte_order;
|
||||
|
||||
try {
|
||||
const std::string input_data{read_from_input_queue(sizeof(size_in_network_byte_order))};
|
||||
size_in_network_byte_order = *reinterpret_cast<const uint32_t*>(input_data.data());
|
||||
} catch (const osmium::pbf_error&) {
|
||||
return 0; // EOF
|
||||
}
|
||||
|
||||
const uint32_t size = ntohl(size_in_network_byte_order);
|
||||
if (size > static_cast<uint32_t>(max_blob_header_size)) {
|
||||
throw osmium::pbf_error{"invalid BlobHeader size (> max_blob_header_size)"};
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the BlobHeader. Make sure it contains the expected
|
||||
* type. Return the size of the following Blob.
|
||||
*/
|
||||
size_t decode_blob_header(protozero::pbf_message<FileFormat::BlobHeader>&& pbf_blob_header, const char* expected_type) {
|
||||
protozero::data_view blob_header_type;
|
||||
size_t blob_header_datasize = 0;
|
||||
|
||||
while (pbf_blob_header.next()) {
|
||||
switch (pbf_blob_header.tag()) {
|
||||
case FileFormat::BlobHeader::required_string_type:
|
||||
blob_header_type = pbf_blob_header.get_view();
|
||||
break;
|
||||
case FileFormat::BlobHeader::required_int32_datasize:
|
||||
blob_header_datasize = pbf_blob_header.get_int32();
|
||||
break;
|
||||
default:
|
||||
pbf_blob_header.skip();
|
||||
}
|
||||
}
|
||||
|
||||
if (blob_header_datasize == 0) {
|
||||
throw osmium::pbf_error{"PBF format error: BlobHeader.datasize missing or zero."};
|
||||
}
|
||||
|
||||
if (std::strncmp(expected_type, blob_header_type.data(), blob_header_type.size())) {
|
||||
throw osmium::pbf_error{"blob does not have expected type (OSMHeader in first blob, OSMData in following blobs)"};
|
||||
}
|
||||
|
||||
return blob_header_datasize;
|
||||
}
|
||||
|
||||
size_t check_type_and_get_blob_size(const char* expected_type) {
|
||||
assert(expected_type);
|
||||
|
||||
const auto size = read_blob_header_size_from_file();
|
||||
if (size == 0) { // EOF
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::string blob_header{read_from_input_queue(size)};
|
||||
|
||||
return decode_blob_header(protozero::pbf_message<FileFormat::BlobHeader>(blob_header), expected_type);
|
||||
}
|
||||
|
||||
std::string read_from_input_queue_with_check(size_t size) {
|
||||
if (size > max_uncompressed_blob_size) {
|
||||
throw osmium::pbf_error{std::string{"invalid blob size: "} +
|
||||
std::to_string(size)};
|
||||
}
|
||||
return read_from_input_queue(size);
|
||||
}
|
||||
|
||||
// Parse the header in the PBF OSMHeader blob.
|
||||
void parse_header_blob() {
|
||||
const auto size = check_type_and_get_blob_size("OSMHeader");
|
||||
osmium::io::Header header{decode_header(read_from_input_queue_with_check(size))};
|
||||
set_header_value(header);
|
||||
}
|
||||
|
||||
void parse_data_blobs() {
|
||||
while (const auto size = check_type_and_get_blob_size("OSMData")) {
|
||||
std::string input_buffer{read_from_input_queue_with_check(size)};
|
||||
|
||||
PBFDataBlobDecoder data_blob_parser{std::move(input_buffer), read_types(), read_metadata()};
|
||||
|
||||
if (osmium::config::use_pool_threads_for_pbf_parsing()) {
|
||||
send_to_output_queue(get_pool().submit(std::move(data_blob_parser)));
|
||||
} else {
|
||||
send_to_output_queue(data_blob_parser());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit PBFParser(parser_arguments& args) :
|
||||
Parser(args),
|
||||
m_input_buffer() {
|
||||
}
|
||||
|
||||
~PBFParser() noexcept final = default;
|
||||
|
||||
void run() final {
|
||||
osmium::thread::set_thread_name("_osmium_pbf_in");
|
||||
|
||||
parse_header_blob();
|
||||
|
||||
if (read_types() != osmium::osm_entity_bits::nothing) {
|
||||
parse_data_blobs();
|
||||
}
|
||||
}
|
||||
|
||||
}; // class PBFParser
|
||||
|
||||
// we want the register_parser() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_pbf_parser = ParserFactory::instance().register_parser(
|
||||
file_format::pbf,
|
||||
[](parser_arguments& args) {
|
||||
return std::unique_ptr<Parser>(new PBFParser{args});
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_pbf_parser() noexcept {
|
||||
return registered_pbf_parser;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_PBF_INPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,661 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_PBF_OUTPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_PBF_OUTPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <protozero/pbf_builder.hpp>
|
||||
#include <protozero/pbf_writer.hpp>
|
||||
#include <protozero/types.hpp>
|
||||
|
||||
#include <osmium/handler.hpp>
|
||||
#include <osmium/io/detail/output_format.hpp>
|
||||
#include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/detail/protobuf_tags.hpp>
|
||||
#include <osmium/io/detail/queue_util.hpp>
|
||||
#include <osmium/io/detail/string_table.hpp>
|
||||
#include <osmium/io/detail/zlib.hpp>
|
||||
#include <osmium/io/file.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/memory/item_iterator.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/node_ref.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/thread/pool.hpp>
|
||||
#include <osmium/util/cast.hpp>
|
||||
#include <osmium/util/delta.hpp>
|
||||
#include <osmium/visitor.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct pbf_output_options {
|
||||
|
||||
/// Should nodes be encoded in DenseNodes?
|
||||
bool use_dense_nodes;
|
||||
|
||||
/**
|
||||
* Should the PBF blobs contain zlib compressed data?
|
||||
*
|
||||
* The zlib compression is optional, it's possible to store the
|
||||
* blobs in raw format. Disabling the compression can improve
|
||||
* the writing speed a little but the output will be 2x to 3x
|
||||
* bigger.
|
||||
*/
|
||||
bool use_compression;
|
||||
|
||||
/// Should metadata of objects be written?
|
||||
bool add_metadata;
|
||||
|
||||
/// Add the "HistoricalInformation" header flag.
|
||||
bool add_historical_information_flag;
|
||||
|
||||
/// Should the visible flag be added to all OSM objects?
|
||||
bool add_visible_flag;
|
||||
|
||||
/// Should node locations be added to ways?
|
||||
bool locations_on_ways;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Maximum number of items in a primitive block.
|
||||
*
|
||||
* The uncompressed length of a Blob *should* be less
|
||||
* than 16 megabytes and *must* be less than 32 megabytes.
|
||||
*
|
||||
* A block may contain any number of entities, as long as
|
||||
* the size limits for the surrounding blob are obeyed.
|
||||
* However, for simplicity, the current Osmosis (0.38)
|
||||
* as well as Osmium implementation always
|
||||
* uses at most 8k entities in a block.
|
||||
*/
|
||||
constexpr int32_t max_entities_per_block = 8000;
|
||||
|
||||
constexpr int location_granularity = 100;
|
||||
|
||||
/**
|
||||
* convert a double lat or lon value to an int, respecting the granularity
|
||||
*/
|
||||
inline int64_t lonlat2int(double lonlat) {
|
||||
return static_cast<int64_t>(std::round(lonlat * lonlat_resolution / location_granularity));
|
||||
}
|
||||
|
||||
enum class pbf_blob_type {
|
||||
header = 0,
|
||||
data = 1
|
||||
};
|
||||
|
||||
class SerializeBlob {
|
||||
|
||||
std::string m_msg;
|
||||
|
||||
pbf_blob_type m_blob_type;
|
||||
|
||||
bool m_use_compression;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Initialize a blob serializer.
|
||||
*
|
||||
* @param msg Protobuf-message containing the blob data
|
||||
* @param type Type of blob.
|
||||
* @param use_compression Should the output be compressed using
|
||||
* zlib?
|
||||
*/
|
||||
SerializeBlob(std::string&& msg, pbf_blob_type type, bool use_compression) :
|
||||
m_msg(std::move(msg)),
|
||||
m_blob_type(type),
|
||||
m_use_compression(use_compression) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a protobuf message into a Blob, optionally apply
|
||||
* compression and return it together with a BlobHeader ready
|
||||
* to be written to a file.
|
||||
*/
|
||||
std::string operator()() {
|
||||
assert(m_msg.size() <= max_uncompressed_blob_size);
|
||||
|
||||
std::string blob_data;
|
||||
protozero::pbf_builder<FileFormat::Blob> pbf_blob{blob_data};
|
||||
|
||||
if (m_use_compression) {
|
||||
pbf_blob.add_int32(FileFormat::Blob::optional_int32_raw_size, int32_t(m_msg.size()));
|
||||
pbf_blob.add_bytes(FileFormat::Blob::optional_bytes_zlib_data, osmium::io::detail::zlib_compress(m_msg));
|
||||
} else {
|
||||
pbf_blob.add_bytes(FileFormat::Blob::optional_bytes_raw, m_msg);
|
||||
}
|
||||
|
||||
std::string blob_header_data;
|
||||
protozero::pbf_builder<FileFormat::BlobHeader> pbf_blob_header{blob_header_data};
|
||||
|
||||
pbf_blob_header.add_string(FileFormat::BlobHeader::required_string_type, m_blob_type == pbf_blob_type::data ? "OSMData" : "OSMHeader");
|
||||
pbf_blob_header.add_int32(FileFormat::BlobHeader::required_int32_datasize, static_cast_with_assert<int32_t>(blob_data.size()));
|
||||
|
||||
const uint32_t sz = htonl(static_cast_with_assert<uint32_t>(blob_header_data.size()));
|
||||
|
||||
// write to output: the 4-byte BlobHeader-Size followed by the BlobHeader followed by the Blob
|
||||
std::string output;
|
||||
output.reserve(sizeof(sz) + blob_header_data.size() + blob_data.size());
|
||||
output.append(reinterpret_cast<const char*>(&sz), sizeof(sz));
|
||||
output.append(blob_header_data);
|
||||
output.append(blob_data);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}; // class SerializeBlob
|
||||
|
||||
/**
|
||||
* Contains the code to pack any number of nodes into a DenseNode
|
||||
* structure.
|
||||
*
|
||||
* Because this needs to allocate a lot of memory on the heap,
|
||||
* only one object of this class will be created and then re-used
|
||||
* after calling clear() on it.
|
||||
*/
|
||||
class DenseNodes {
|
||||
|
||||
StringTable& m_stringtable;
|
||||
|
||||
std::vector<int64_t> m_ids;
|
||||
|
||||
std::vector<int32_t> m_versions;
|
||||
std::vector<int64_t> m_timestamps;
|
||||
std::vector<int64_t> m_changesets;
|
||||
std::vector<int32_t> m_uids;
|
||||
std::vector<int32_t> m_user_sids;
|
||||
std::vector<bool> m_visibles;
|
||||
|
||||
std::vector<int64_t> m_lats;
|
||||
std::vector<int64_t> m_lons;
|
||||
std::vector<int32_t> m_tags;
|
||||
|
||||
osmium::util::DeltaEncode<object_id_type, int64_t> m_delta_id;
|
||||
|
||||
osmium::util::DeltaEncode<uint32_t, int64_t> m_delta_timestamp;
|
||||
osmium::util::DeltaEncode<changeset_id_type, int64_t> m_delta_changeset;
|
||||
osmium::util::DeltaEncode<user_id_type, int32_t> m_delta_uid;
|
||||
osmium::util::DeltaEncode<uint32_t, int32_t> m_delta_user_sid;
|
||||
|
||||
osmium::util::DeltaEncode<int64_t, int64_t> m_delta_lat;
|
||||
osmium::util::DeltaEncode<int64_t, int64_t> m_delta_lon;
|
||||
|
||||
const pbf_output_options& m_options;
|
||||
|
||||
public:
|
||||
|
||||
DenseNodes(StringTable& stringtable, const pbf_output_options& options) :
|
||||
m_stringtable(stringtable),
|
||||
m_options(options) {
|
||||
}
|
||||
|
||||
/// Clear object for re-use. Keep the allocated memory.
|
||||
void clear() {
|
||||
m_ids.clear();
|
||||
|
||||
m_versions.clear();
|
||||
m_timestamps.clear();
|
||||
m_changesets.clear();
|
||||
m_uids.clear();
|
||||
m_user_sids.clear();
|
||||
m_visibles.clear();
|
||||
|
||||
m_lats.clear();
|
||||
m_lons.clear();
|
||||
m_tags.clear();
|
||||
|
||||
m_delta_id.clear();
|
||||
|
||||
m_delta_timestamp.clear();
|
||||
m_delta_changeset.clear();
|
||||
m_delta_uid.clear();
|
||||
m_delta_user_sid.clear();
|
||||
|
||||
m_delta_lat.clear();
|
||||
m_delta_lon.clear();
|
||||
}
|
||||
|
||||
std::size_t size() const {
|
||||
return m_ids.size() * 3 * sizeof(int64_t);
|
||||
}
|
||||
|
||||
void add_node(const osmium::Node& node) {
|
||||
m_ids.push_back(m_delta_id.update(node.id()));
|
||||
|
||||
if (m_options.add_metadata) {
|
||||
m_versions.push_back(static_cast_with_assert<int32_t>(node.version()));
|
||||
m_timestamps.push_back(m_delta_timestamp.update(uint32_t(node.timestamp())));
|
||||
m_changesets.push_back(m_delta_changeset.update(node.changeset()));
|
||||
m_uids.push_back(m_delta_uid.update(node.uid()));
|
||||
m_user_sids.push_back(m_delta_user_sid.update(m_stringtable.add(node.user())));
|
||||
if (m_options.add_visible_flag) {
|
||||
m_visibles.push_back(node.visible());
|
||||
}
|
||||
}
|
||||
|
||||
m_lats.push_back(m_delta_lat.update(lonlat2int(node.location().lat_without_check())));
|
||||
m_lons.push_back(m_delta_lon.update(lonlat2int(node.location().lon_without_check())));
|
||||
|
||||
for (const auto& tag : node.tags()) {
|
||||
m_tags.push_back(static_cast_with_assert<int32_t>(m_stringtable.add(tag.key())));
|
||||
m_tags.push_back(static_cast_with_assert<int32_t>(m_stringtable.add(tag.value())));
|
||||
}
|
||||
m_tags.push_back(0);
|
||||
}
|
||||
|
||||
std::string serialize() const {
|
||||
std::string data;
|
||||
protozero::pbf_builder<OSMFormat::DenseNodes> pbf_dense_nodes{data};
|
||||
|
||||
pbf_dense_nodes.add_packed_sint64(OSMFormat::DenseNodes::packed_sint64_id, m_ids.cbegin(), m_ids.cend());
|
||||
|
||||
if (m_options.add_metadata) {
|
||||
protozero::pbf_builder<OSMFormat::DenseInfo> pbf_dense_info{pbf_dense_nodes, OSMFormat::DenseNodes::optional_DenseInfo_denseinfo};
|
||||
pbf_dense_info.add_packed_int32(OSMFormat::DenseInfo::packed_int32_version, m_versions.cbegin(), m_versions.cend());
|
||||
pbf_dense_info.add_packed_sint64(OSMFormat::DenseInfo::packed_sint64_timestamp, m_timestamps.cbegin(), m_timestamps.cend());
|
||||
pbf_dense_info.add_packed_sint64(OSMFormat::DenseInfo::packed_sint64_changeset, m_changesets.cbegin(), m_changesets.cend());
|
||||
pbf_dense_info.add_packed_sint32(OSMFormat::DenseInfo::packed_sint32_uid, m_uids.cbegin(), m_uids.cend());
|
||||
pbf_dense_info.add_packed_sint32(OSMFormat::DenseInfo::packed_sint32_user_sid, m_user_sids.cbegin(), m_user_sids.cend());
|
||||
|
||||
if (m_options.add_visible_flag) {
|
||||
pbf_dense_info.add_packed_bool(OSMFormat::DenseInfo::packed_bool_visible, m_visibles.cbegin(), m_visibles.cend());
|
||||
}
|
||||
}
|
||||
|
||||
pbf_dense_nodes.add_packed_sint64(OSMFormat::DenseNodes::packed_sint64_lat, m_lats.cbegin(), m_lats.cend());
|
||||
pbf_dense_nodes.add_packed_sint64(OSMFormat::DenseNodes::packed_sint64_lon, m_lons.cbegin(), m_lons.cend());
|
||||
|
||||
pbf_dense_nodes.add_packed_int32(OSMFormat::DenseNodes::packed_int32_keys_vals, m_tags.cbegin(), m_tags.cend());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}; // class DenseNodes
|
||||
|
||||
class PrimitiveBlock {
|
||||
|
||||
std::string m_pbf_primitive_group_data;
|
||||
protozero::pbf_builder<OSMFormat::PrimitiveGroup> m_pbf_primitive_group;
|
||||
StringTable m_stringtable;
|
||||
DenseNodes m_dense_nodes;
|
||||
OSMFormat::PrimitiveGroup m_type;
|
||||
int m_count;
|
||||
|
||||
public:
|
||||
|
||||
explicit PrimitiveBlock(const pbf_output_options& options) :
|
||||
m_pbf_primitive_group_data(),
|
||||
m_pbf_primitive_group(m_pbf_primitive_group_data),
|
||||
m_stringtable(),
|
||||
m_dense_nodes(m_stringtable, options),
|
||||
m_type(OSMFormat::PrimitiveGroup::unknown),
|
||||
m_count(0) {
|
||||
}
|
||||
|
||||
const std::string& group_data() {
|
||||
if (type() == OSMFormat::PrimitiveGroup::optional_DenseNodes_dense) {
|
||||
m_pbf_primitive_group.add_message(OSMFormat::PrimitiveGroup::optional_DenseNodes_dense, m_dense_nodes.serialize());
|
||||
}
|
||||
return m_pbf_primitive_group_data;
|
||||
}
|
||||
|
||||
void reset(OSMFormat::PrimitiveGroup type) {
|
||||
m_pbf_primitive_group_data.clear();
|
||||
m_stringtable.clear();
|
||||
m_dense_nodes.clear();
|
||||
m_type = type;
|
||||
m_count = 0;
|
||||
}
|
||||
|
||||
void write_stringtable(protozero::pbf_builder<OSMFormat::StringTable>& pbf_string_table) {
|
||||
for (const char* s : m_stringtable) {
|
||||
pbf_string_table.add_bytes(OSMFormat::StringTable::repeated_bytes_s, s);
|
||||
}
|
||||
}
|
||||
|
||||
protozero::pbf_builder<OSMFormat::PrimitiveGroup>& group() {
|
||||
++m_count;
|
||||
return m_pbf_primitive_group;
|
||||
}
|
||||
|
||||
void add_dense_node(const osmium::Node& node) {
|
||||
m_dense_nodes.add_node(node);
|
||||
++m_count;
|
||||
}
|
||||
|
||||
uint32_t store_in_stringtable(const char* s) {
|
||||
return m_stringtable.add(s);
|
||||
}
|
||||
|
||||
int count() const {
|
||||
return m_count;
|
||||
}
|
||||
|
||||
OSMFormat::PrimitiveGroup type() const {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
std::size_t size() const {
|
||||
return m_pbf_primitive_group_data.size() + m_stringtable.size() + m_dense_nodes.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* The output buffer (block) will be filled to about
|
||||
* 95% and then written to disk. This leaves more than
|
||||
* enough space for the string table (which typically
|
||||
* needs about 0.1 to 0.3% of the block size).
|
||||
*/
|
||||
constexpr static std::size_t max_used_blob_size = max_uncompressed_blob_size * 95 / 100;
|
||||
|
||||
bool can_add(OSMFormat::PrimitiveGroup type) const {
|
||||
if (type != m_type) {
|
||||
return false;
|
||||
}
|
||||
if (count() >= max_entities_per_block) {
|
||||
return false;
|
||||
}
|
||||
return size() < max_used_blob_size;
|
||||
}
|
||||
|
||||
}; // class PrimitiveBlock
|
||||
|
||||
class PBFOutputFormat : public osmium::io::detail::OutputFormat, public osmium::handler::Handler {
|
||||
|
||||
pbf_output_options m_options;
|
||||
|
||||
PrimitiveBlock m_primitive_block;
|
||||
|
||||
void store_primitive_block() {
|
||||
if (m_primitive_block.count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string primitive_block_data;
|
||||
protozero::pbf_builder<OSMFormat::PrimitiveBlock> primitive_block{primitive_block_data};
|
||||
|
||||
{
|
||||
protozero::pbf_builder<OSMFormat::StringTable> pbf_string_table{primitive_block, OSMFormat::PrimitiveBlock::required_StringTable_stringtable};
|
||||
m_primitive_block.write_stringtable(pbf_string_table);
|
||||
}
|
||||
|
||||
primitive_block.add_message(OSMFormat::PrimitiveBlock::repeated_PrimitiveGroup_primitivegroup, m_primitive_block.group_data());
|
||||
|
||||
m_output_queue.push(m_pool.submit(
|
||||
SerializeBlob{std::move(primitive_block_data),
|
||||
pbf_blob_type::data,
|
||||
m_options.use_compression}
|
||||
));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void add_meta(const osmium::OSMObject& object, T& pbf_object) {
|
||||
{
|
||||
protozero::packed_field_uint32 field{pbf_object, protozero::pbf_tag_type(T::enum_type::packed_uint32_keys)};
|
||||
for (const auto& tag : object.tags()) {
|
||||
field.add_element(m_primitive_block.store_in_stringtable(tag.key()));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
protozero::packed_field_uint32 field{pbf_object, protozero::pbf_tag_type(T::enum_type::packed_uint32_vals)};
|
||||
for (const auto& tag : object.tags()) {
|
||||
field.add_element(m_primitive_block.store_in_stringtable(tag.value()));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_options.add_metadata) {
|
||||
protozero::pbf_builder<OSMFormat::Info> pbf_info{pbf_object, T::enum_type::optional_Info_info};
|
||||
|
||||
pbf_info.add_int32(OSMFormat::Info::optional_int32_version, static_cast_with_assert<int32_t>(object.version()));
|
||||
pbf_info.add_int64(OSMFormat::Info::optional_int64_timestamp, uint32_t(object.timestamp()));
|
||||
pbf_info.add_int64(OSMFormat::Info::optional_int64_changeset, object.changeset());
|
||||
pbf_info.add_int32(OSMFormat::Info::optional_int32_uid, static_cast_with_assert<int32_t>(object.uid()));
|
||||
pbf_info.add_uint32(OSMFormat::Info::optional_uint32_user_sid, m_primitive_block.store_in_stringtable(object.user()));
|
||||
if (m_options.add_visible_flag) {
|
||||
pbf_info.add_bool(OSMFormat::Info::optional_bool_visible, object.visible());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void switch_primitive_block_type(OSMFormat::PrimitiveGroup type) {
|
||||
if (!m_primitive_block.can_add(type)) {
|
||||
store_primitive_block();
|
||||
m_primitive_block.reset(type);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
PBFOutputFormat(osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) :
|
||||
OutputFormat(pool, output_queue),
|
||||
m_options(),
|
||||
m_primitive_block(m_options) {
|
||||
m_options.use_dense_nodes = file.is_not_false("pbf_dense_nodes");
|
||||
m_options.use_compression = file.get("pbf_compression") != "none" && file.is_not_false("pbf_compression");
|
||||
m_options.add_metadata = file.is_not_false("pbf_add_metadata") && file.is_not_false("add_metadata");
|
||||
m_options.add_historical_information_flag = file.has_multiple_object_versions();
|
||||
m_options.add_visible_flag = file.has_multiple_object_versions();
|
||||
m_options.locations_on_ways = file.is_true("locations_on_ways");
|
||||
}
|
||||
|
||||
PBFOutputFormat(const PBFOutputFormat&) = delete;
|
||||
PBFOutputFormat& operator=(const PBFOutputFormat&) = delete;
|
||||
|
||||
~PBFOutputFormat() noexcept final = default;
|
||||
|
||||
void write_header(const osmium::io::Header& header) final {
|
||||
std::string data;
|
||||
protozero::pbf_builder<OSMFormat::HeaderBlock> pbf_header_block{data};
|
||||
|
||||
if (!header.boxes().empty()) {
|
||||
protozero::pbf_builder<OSMFormat::HeaderBBox> pbf_header_bbox{pbf_header_block, OSMFormat::HeaderBlock::optional_HeaderBBox_bbox};
|
||||
|
||||
osmium::Box box = header.joined_boxes();
|
||||
pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_left, int64_t(box.bottom_left().lon() * lonlat_resolution));
|
||||
pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_right, int64_t(box.top_right().lon() * lonlat_resolution));
|
||||
pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_top, int64_t(box.top_right().lat() * lonlat_resolution));
|
||||
pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_bottom, int64_t(box.bottom_left().lat() * lonlat_resolution));
|
||||
}
|
||||
|
||||
pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_required_features, "OsmSchema-V0.6");
|
||||
|
||||
if (m_options.use_dense_nodes) {
|
||||
pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_required_features, "DenseNodes");
|
||||
}
|
||||
|
||||
if (m_options.add_historical_information_flag) {
|
||||
pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_required_features, "HistoricalInformation");
|
||||
}
|
||||
|
||||
if (m_options.locations_on_ways) {
|
||||
pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_optional_features, "LocationsOnWays");
|
||||
}
|
||||
|
||||
pbf_header_block.add_string(OSMFormat::HeaderBlock::optional_string_writingprogram, header.get("generator"));
|
||||
|
||||
const std::string osmosis_replication_timestamp{header.get("osmosis_replication_timestamp")};
|
||||
if (!osmosis_replication_timestamp.empty()) {
|
||||
osmium::Timestamp ts{osmosis_replication_timestamp.c_str()};
|
||||
pbf_header_block.add_int64(OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp, uint32_t(ts));
|
||||
}
|
||||
|
||||
const std::string osmosis_replication_sequence_number{header.get("osmosis_replication_sequence_number")};
|
||||
if (!osmosis_replication_sequence_number.empty()) {
|
||||
pbf_header_block.add_int64(OSMFormat::HeaderBlock::optional_int64_osmosis_replication_sequence_number, std::atoll(osmosis_replication_sequence_number.c_str()));
|
||||
}
|
||||
|
||||
const std::string osmosis_replication_base_url{header.get("osmosis_replication_base_url")};
|
||||
if (!osmosis_replication_base_url.empty()) {
|
||||
pbf_header_block.add_string(OSMFormat::HeaderBlock::optional_string_osmosis_replication_base_url, osmosis_replication_base_url);
|
||||
}
|
||||
|
||||
m_output_queue.push(m_pool.submit(
|
||||
SerializeBlob{std::move(data),
|
||||
pbf_blob_type::header,
|
||||
m_options.use_compression}
|
||||
));
|
||||
}
|
||||
|
||||
void write_buffer(osmium::memory::Buffer&& buffer) final {
|
||||
osmium::apply(buffer.cbegin(), buffer.cend(), *this);
|
||||
}
|
||||
|
||||
void write_end() final {
|
||||
store_primitive_block();
|
||||
}
|
||||
|
||||
void node(const osmium::Node& node) {
|
||||
if (m_options.use_dense_nodes) {
|
||||
switch_primitive_block_type(OSMFormat::PrimitiveGroup::optional_DenseNodes_dense);
|
||||
m_primitive_block.add_dense_node(node);
|
||||
return;
|
||||
}
|
||||
|
||||
switch_primitive_block_type(OSMFormat::PrimitiveGroup::repeated_Node_nodes);
|
||||
protozero::pbf_builder<OSMFormat::Node> pbf_node{m_primitive_block.group(), OSMFormat::PrimitiveGroup::repeated_Node_nodes};
|
||||
|
||||
pbf_node.add_sint64(OSMFormat::Node::required_sint64_id, node.id());
|
||||
add_meta(node, pbf_node);
|
||||
|
||||
pbf_node.add_sint64(OSMFormat::Node::required_sint64_lat, lonlat2int(node.location().lat_without_check()));
|
||||
pbf_node.add_sint64(OSMFormat::Node::required_sint64_lon, lonlat2int(node.location().lon_without_check()));
|
||||
}
|
||||
|
||||
void way(const osmium::Way& way) {
|
||||
switch_primitive_block_type(OSMFormat::PrimitiveGroup::repeated_Way_ways);
|
||||
protozero::pbf_builder<OSMFormat::Way> pbf_way{m_primitive_block.group(), OSMFormat::PrimitiveGroup::repeated_Way_ways};
|
||||
|
||||
pbf_way.add_int64(OSMFormat::Way::required_int64_id, way.id());
|
||||
add_meta(way, pbf_way);
|
||||
|
||||
{
|
||||
osmium::util::DeltaEncode<object_id_type, int64_t> delta_id;
|
||||
protozero::packed_field_sint64 field{pbf_way, protozero::pbf_tag_type(OSMFormat::Way::packed_sint64_refs)};
|
||||
for (const auto& node_ref : way.nodes()) {
|
||||
field.add_element(delta_id.update(node_ref.ref()));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_options.locations_on_ways) {
|
||||
{
|
||||
osmium::util::DeltaEncode<int64_t, int64_t> delta_id;
|
||||
protozero::packed_field_sint64 field{pbf_way, protozero::pbf_tag_type(OSMFormat::Way::packed_sint64_lon)};
|
||||
for (const auto& node_ref : way.nodes()) {
|
||||
field.add_element(delta_id.update(lonlat2int(node_ref.location().lon_without_check())));
|
||||
}
|
||||
}
|
||||
{
|
||||
osmium::util::DeltaEncode<int64_t, int64_t> delta_id;
|
||||
protozero::packed_field_sint64 field{pbf_way, protozero::pbf_tag_type(OSMFormat::Way::packed_sint64_lat)};
|
||||
for (const auto& node_ref : way.nodes()) {
|
||||
field.add_element(delta_id.update(lonlat2int(node_ref.location().lat_without_check())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) {
|
||||
switch_primitive_block_type(OSMFormat::PrimitiveGroup::repeated_Relation_relations);
|
||||
protozero::pbf_builder<OSMFormat::Relation> pbf_relation{m_primitive_block.group(), OSMFormat::PrimitiveGroup::repeated_Relation_relations};
|
||||
|
||||
pbf_relation.add_int64(OSMFormat::Relation::required_int64_id, relation.id());
|
||||
add_meta(relation, pbf_relation);
|
||||
|
||||
{
|
||||
protozero::packed_field_int32 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_int32_roles_sid)};
|
||||
for (const auto& member : relation.members()) {
|
||||
field.add_element(m_primitive_block.store_in_stringtable(member.role()));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
osmium::util::DeltaEncode<object_id_type, int64_t> delta_id;
|
||||
protozero::packed_field_sint64 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_sint64_memids)};
|
||||
for (const auto& member : relation.members()) {
|
||||
field.add_element(delta_id.update(member.ref()));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
protozero::packed_field_int32 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_MemberType_types)};
|
||||
for (const auto& member : relation.members()) {
|
||||
field.add_element(int32_t(osmium::item_type_to_nwr_index(member.type())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // class PBFOutputFormat
|
||||
|
||||
// we want the register_output_format() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_pbf_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::pbf,
|
||||
[](osmium::thread::Pool& pool, const osmium::io::File& file, future_string_queue_type& output_queue) {
|
||||
return new osmium::io::detail::PBFOutputFormat{pool, file, output_queue};
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_pbf_output() noexcept {
|
||||
return registered_pbf_output;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_PBF_OUTPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,172 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_PROTOBUF_TAGS_HPP
|
||||
#define OSMIUM_IO_DETAIL_PROTOBUF_TAGS_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <protozero/types.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// directly translated from
|
||||
// https://github.com/scrosby/OSM-binary/blob/master/src/fileformat.proto
|
||||
|
||||
namespace FileFormat {
|
||||
|
||||
enum class Blob : protozero::pbf_tag_type {
|
||||
optional_bytes_raw = 1,
|
||||
optional_int32_raw_size = 2,
|
||||
optional_bytes_zlib_data = 3,
|
||||
optional_bytes_lzma_data = 4
|
||||
};
|
||||
|
||||
enum class BlobHeader : protozero::pbf_tag_type {
|
||||
required_string_type = 1,
|
||||
optional_bytes_indexdata = 2,
|
||||
required_int32_datasize = 3
|
||||
};
|
||||
|
||||
} // namespace FileFormat
|
||||
|
||||
// directly translated from
|
||||
// https://github.com/scrosby/OSM-binary/blob/master/src/osmformat.proto
|
||||
|
||||
namespace OSMFormat {
|
||||
|
||||
enum class HeaderBlock : protozero::pbf_tag_type {
|
||||
optional_HeaderBBox_bbox = 1,
|
||||
repeated_string_required_features = 4,
|
||||
repeated_string_optional_features = 5,
|
||||
optional_string_writingprogram = 16,
|
||||
optional_string_source = 17,
|
||||
optional_int64_osmosis_replication_timestamp = 32,
|
||||
optional_int64_osmosis_replication_sequence_number = 33,
|
||||
optional_string_osmosis_replication_base_url = 34
|
||||
};
|
||||
|
||||
enum class HeaderBBox : protozero::pbf_tag_type {
|
||||
required_sint64_left = 1,
|
||||
required_sint64_right = 2,
|
||||
required_sint64_top = 3,
|
||||
required_sint64_bottom = 4
|
||||
};
|
||||
|
||||
enum class PrimitiveBlock : protozero::pbf_tag_type {
|
||||
required_StringTable_stringtable = 1,
|
||||
repeated_PrimitiveGroup_primitivegroup = 2,
|
||||
optional_int32_granularity = 17,
|
||||
optional_int32_date_granularity = 18,
|
||||
optional_int64_lat_offset = 19,
|
||||
optional_int64_lon_offset = 20
|
||||
};
|
||||
|
||||
enum class PrimitiveGroup : protozero::pbf_tag_type {
|
||||
unknown = 0,
|
||||
repeated_Node_nodes = 1,
|
||||
optional_DenseNodes_dense = 2,
|
||||
repeated_Way_ways = 3,
|
||||
repeated_Relation_relations = 4,
|
||||
repeated_ChangeSet_changesets = 5
|
||||
};
|
||||
|
||||
enum class StringTable : protozero::pbf_tag_type {
|
||||
repeated_bytes_s = 1
|
||||
};
|
||||
|
||||
enum class Info : protozero::pbf_tag_type {
|
||||
optional_int32_version = 1,
|
||||
optional_int64_timestamp = 2,
|
||||
optional_int64_changeset = 3,
|
||||
optional_int32_uid = 4,
|
||||
optional_uint32_user_sid = 5,
|
||||
optional_bool_visible = 6
|
||||
};
|
||||
|
||||
enum class DenseInfo : protozero::pbf_tag_type {
|
||||
packed_int32_version = 1,
|
||||
packed_sint64_timestamp = 2,
|
||||
packed_sint64_changeset = 3,
|
||||
packed_sint32_uid = 4,
|
||||
packed_sint32_user_sid = 5,
|
||||
packed_bool_visible = 6
|
||||
};
|
||||
|
||||
enum class Node : protozero::pbf_tag_type {
|
||||
required_sint64_id = 1,
|
||||
packed_uint32_keys = 2,
|
||||
packed_uint32_vals = 3,
|
||||
optional_Info_info = 4,
|
||||
required_sint64_lat = 8,
|
||||
required_sint64_lon = 9
|
||||
};
|
||||
|
||||
enum class DenseNodes : protozero::pbf_tag_type {
|
||||
packed_sint64_id = 1,
|
||||
optional_DenseInfo_denseinfo = 5,
|
||||
packed_sint64_lat = 8,
|
||||
packed_sint64_lon = 9,
|
||||
packed_int32_keys_vals = 10
|
||||
};
|
||||
|
||||
enum class Way : protozero::pbf_tag_type {
|
||||
required_int64_id = 1,
|
||||
packed_uint32_keys = 2,
|
||||
packed_uint32_vals = 3,
|
||||
optional_Info_info = 4,
|
||||
packed_sint64_refs = 8,
|
||||
packed_sint64_lat = 9,
|
||||
packed_sint64_lon = 10
|
||||
};
|
||||
|
||||
enum class Relation : protozero::pbf_tag_type {
|
||||
required_int64_id = 1,
|
||||
packed_uint32_keys = 2,
|
||||
packed_uint32_vals = 3,
|
||||
optional_Info_info = 4,
|
||||
packed_int32_roles_sid = 8,
|
||||
packed_sint64_memids = 9,
|
||||
packed_MemberType_types = 10
|
||||
};
|
||||
|
||||
} // namespace OSMFormat
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_PROTOBUF_TAGS_HPP
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user