Large refactoring of the geometry code
Pulls apart the different concerns: * geometry generation from OSM data * geometry processing (like merging and splitting linestrings) * conversion to WKB For all of this to work we switch to C++17 as minimum requirement. This also adds some code (in src/geom-boost-adaptor.hpp) that adapts our data structures to boost::geometry. This is not used yet, but can in the future be used for more complex geometry processing.HEAD
parent
02d7d7de09
commit
943ca1ab25
|
@ -77,7 +77,7 @@ jobs:
|
|||
LUAJIT_OPTION: OFF
|
||||
POSTGRESQL_VERSION: 10
|
||||
POSTGIS_VERSION: 3
|
||||
CPP_VERSION: 14
|
||||
CPP_VERSION: 17
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
steps:
|
||||
|
@ -96,7 +96,7 @@ jobs:
|
|||
LUAJIT_OPTION: OFF
|
||||
POSTGRESQL_VERSION: 11
|
||||
POSTGIS_VERSION: 2.5
|
||||
CPP_VERSION: 14
|
||||
CPP_VERSION: 17
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
steps:
|
||||
|
@ -114,7 +114,7 @@ jobs:
|
|||
LUAJIT_OPTION: ON
|
||||
POSTGRESQL_VERSION: 12
|
||||
POSTGIS_VERSION: 2.5
|
||||
CPP_VERSION: 14
|
||||
CPP_VERSION: 17
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
steps:
|
||||
|
@ -133,7 +133,7 @@ jobs:
|
|||
LUAJIT_OPTION: ON
|
||||
POSTGRESQL_VERSION: 13
|
||||
POSTGIS_VERSION: 3
|
||||
CPP_VERSION: 14
|
||||
CPP_VERSION: 17
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
steps:
|
||||
|
@ -151,7 +151,7 @@ jobs:
|
|||
LUAJIT_OPTION: OFF
|
||||
POSTGRESQL_VERSION: 13
|
||||
POSTGIS_VERSION: 3
|
||||
CPP_VERSION: 14
|
||||
CPP_VERSION: 17
|
||||
USE_PROJ_LIB: 6
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
|
@ -170,7 +170,7 @@ jobs:
|
|||
LUAJIT_OPTION: OFF
|
||||
POSTGRESQL_VERSION: 13
|
||||
POSTGIS_VERSION: 3
|
||||
CPP_VERSION: 14
|
||||
CPP_VERSION: 17
|
||||
USE_PROJ_LIB: off
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
|
@ -208,7 +208,7 @@ jobs:
|
|||
LUAJIT_OPTION: ON
|
||||
POSTGRESQL_VERSION: 13
|
||||
POSTGIS_VERSION: 2.5
|
||||
CPP_VERSION: 14
|
||||
CPP_VERSION: 17
|
||||
BUILD_TYPE: Release
|
||||
|
||||
steps:
|
||||
|
|
|
@ -38,7 +38,7 @@ if (NOT CMAKE_BUILD_TYPE)
|
|||
endif()
|
||||
|
||||
if (NOT "${CMAKE_CXX_STANDARD}")
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
message(STATUS "Building in C++${CMAKE_CXX_STANDARD} mode")
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
|
|
@ -70,7 +70,7 @@ It also requires access to a database server running
|
|||
[PostGIS](https://www.postgis.net/) 2.2+.
|
||||
|
||||
Make sure you have installed the development packages for the libraries
|
||||
mentioned in the requirements section and a C++ compiler which supports C++14.
|
||||
mentioned in the requirements section and a C++ compiler which supports C++17.
|
||||
We officially support gcc >= 7.0 and clang >= 8.
|
||||
|
||||
To rebuild the included man page you'll need the [pandoc](https://pandoc.org/)
|
||||
|
|
|
@ -8,6 +8,8 @@ target_sources(osm2pgsql_lib PRIVATE
|
|||
expire-tiles.cpp
|
||||
gazetteer-style.cpp
|
||||
geom.cpp
|
||||
geom-from-osm.cpp
|
||||
geom-functions.cpp
|
||||
input.cpp
|
||||
logging.cpp
|
||||
middle.cpp
|
||||
|
@ -18,7 +20,6 @@ target_sources(osm2pgsql_lib PRIVATE
|
|||
options.cpp
|
||||
ordered-index.cpp
|
||||
osmdata.cpp
|
||||
osmium-builder.cpp
|
||||
output-gazetteer.cpp
|
||||
output-null.cpp
|
||||
output-pgsql.cpp
|
||||
|
@ -34,6 +35,7 @@ target_sources(osm2pgsql_lib PRIVATE
|
|||
thread-pool.cpp
|
||||
util.cpp
|
||||
wildcmp.cpp
|
||||
wkb.cpp
|
||||
)
|
||||
|
||||
if (LUA_FOUND OR LUAJIT_FOUND)
|
||||
|
|
|
@ -128,11 +128,10 @@ uint32_t expire_tiles::normalise_tile_x_coord(int x) const
|
|||
void expire_tiles::coords_to_tile(double lon, double lat, double *tilex,
|
||||
double *tiley)
|
||||
{
|
||||
auto const c =
|
||||
projection->target_to_tile(osmium::geom::Coordinates{lon, lat});
|
||||
auto const c = projection->target_to_tile(geom::point_t{lon, lat});
|
||||
|
||||
*tilex = map_width * (0.5 + c.x / EARTH_CIRCUMFERENCE);
|
||||
*tiley = map_width * (0.5 - c.y / EARTH_CIRCUMFERENCE);
|
||||
*tilex = map_width * (0.5 + c.x() / EARTH_CIRCUMFERENCE);
|
||||
*tiley = map_width * (0.5 - c.y() / EARTH_CIRCUMFERENCE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
|
||||
#include "db-copy-mgr.hpp"
|
||||
#include "flex-table-column.hpp"
|
||||
#include "osmium-builder.hpp"
|
||||
#include "pgsql.hpp"
|
||||
#include "reprojection.hpp"
|
||||
#include "thread-pool.hpp"
|
||||
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
|
@ -232,7 +232,7 @@ class table_connection_t
|
|||
public:
|
||||
table_connection_t(flex_table_t *table,
|
||||
std::shared_ptr<db_copy_thread_t> const ©_thread)
|
||||
: m_builder(reprojection::create_projection(table->srid())), m_table(table),
|
||||
: m_proj(reprojection::create_projection(table->srid())), m_table(table),
|
||||
m_target(std::make_shared<db_target_descr_t>(
|
||||
table->name(), table->id_column_names(),
|
||||
table->build_sql_column_list())),
|
||||
|
@ -268,7 +268,11 @@ public:
|
|||
|
||||
void delete_rows_with(osmium::item_type type, osmid_t id);
|
||||
|
||||
geom::osmium_builder_t *get_builder() { return &m_builder; }
|
||||
reprojection const &proj() const noexcept
|
||||
{
|
||||
assert(m_proj);
|
||||
return *m_proj;
|
||||
}
|
||||
|
||||
void task_set(std::future<std::chrono::milliseconds> &&future)
|
||||
{
|
||||
|
@ -278,7 +282,7 @@ public:
|
|||
void task_wait();
|
||||
|
||||
private:
|
||||
geom::osmium_builder_t m_builder;
|
||||
std::shared_ptr<reprojection> m_proj;
|
||||
|
||||
flex_table_t *m_table;
|
||||
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This file is part of osm2pgsql (https://osm2pgsql.org/).
|
||||
*
|
||||
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
|
||||
* For a full list of authors see the git log.
|
||||
*/
|
||||
|
||||
#include "geom-from-osm.hpp"
|
||||
#include "osmtypes.hpp"
|
||||
|
||||
#include <osmium/area/geom_assembler.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace geom {
|
||||
|
||||
geometry_t create_point(osmium::Node const &node)
|
||||
{
|
||||
return geometry_t{point_t{node.location()}};
|
||||
}
|
||||
|
||||
static void fill_point_list(point_list_t *list,
|
||||
osmium::NodeRefList const &nodes)
|
||||
{
|
||||
osmium::Location last{};
|
||||
|
||||
for (auto const &node : nodes) {
|
||||
auto const loc = node.location();
|
||||
if (loc.valid() && loc != last) {
|
||||
list->emplace_back(loc);
|
||||
last = loc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
geometry_t create_linestring(osmium::Way const &way)
|
||||
{
|
||||
geometry_t geom{linestring_t{}};
|
||||
auto &line = geom.get<linestring_t>();
|
||||
|
||||
fill_point_list(&line, way.nodes());
|
||||
|
||||
// Return nullgeom_t if the line geometry is invalid
|
||||
if (line.size() <= 1U) {
|
||||
geom.reset();
|
||||
}
|
||||
|
||||
return geom;
|
||||
}
|
||||
|
||||
geometry_t create_polygon(osmium::Way const &way)
|
||||
{
|
||||
geometry_t geom{polygon_t{}};
|
||||
|
||||
// A closed way with less than 4 nodes can never be a valid polygon
|
||||
if (way.nodes().size() < 4U) {
|
||||
geom.reset();
|
||||
return geom;
|
||||
}
|
||||
|
||||
osmium::area::AssemblerConfig area_config;
|
||||
area_config.ignore_invalid_locations = true;
|
||||
osmium::area::GeomAssembler assembler{area_config};
|
||||
osmium::memory::Buffer area_buffer{1024};
|
||||
|
||||
if (!assembler(way, area_buffer)) {
|
||||
geom.reset();
|
||||
return geom;
|
||||
}
|
||||
|
||||
auto const &area = area_buffer.get<osmium::Area>(0);
|
||||
auto const &ring = *area.begin<osmium::OuterRing>();
|
||||
|
||||
fill_point_list(&geom.get<polygon_t>().outer(), ring);
|
||||
|
||||
return geom;
|
||||
}
|
||||
|
||||
geometry_t create_multilinestring(osmium::memory::Buffer const &ways)
|
||||
{
|
||||
geometry_t geom{multilinestring_t{}};
|
||||
auto &mls = geom.get<multilinestring_t>();
|
||||
|
||||
for (auto const &way : ways.select<osmium::Way>()) {
|
||||
linestring_t line;
|
||||
fill_point_list(&line, way.nodes());
|
||||
if (line.size() >= 2U) {
|
||||
mls.add_geometry(std::move(line));
|
||||
}
|
||||
}
|
||||
|
||||
if (mls.num_geometries() == 0) {
|
||||
geom.reset();
|
||||
}
|
||||
|
||||
return geom;
|
||||
}
|
||||
|
||||
static void fill_polygon(polygon_t *polygon, osmium::Area const &area,
|
||||
osmium::OuterRing const &outer_ring)
|
||||
{
|
||||
assert(polygon->inners().empty());
|
||||
|
||||
for (auto const &nr : outer_ring) {
|
||||
polygon->outer().emplace_back(nr.location());
|
||||
}
|
||||
|
||||
for (auto const &inner_ring : area.inner_rings(outer_ring)) {
|
||||
auto &ring = polygon->inners().emplace_back();
|
||||
for (auto const &nr : inner_ring) {
|
||||
ring.emplace_back(nr.location());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
geometry_t create_multipolygon(osmium::Relation const &relation,
|
||||
osmium::memory::Buffer const &way_buffer)
|
||||
{
|
||||
geometry_t geom{};
|
||||
|
||||
osmium::area::AssemblerConfig area_config;
|
||||
area_config.ignore_invalid_locations = true;
|
||||
osmium::area::GeomAssembler assembler{area_config};
|
||||
osmium::memory::Buffer area_buffer{1024};
|
||||
|
||||
if (!assembler(relation, way_buffer, area_buffer)) {
|
||||
return geom;
|
||||
}
|
||||
|
||||
auto const &area = area_buffer.get<osmium::Area>(0);
|
||||
|
||||
if (area.is_multipolygon()) {
|
||||
auto &multipolygon = geom.set<multipolygon_t>();
|
||||
|
||||
for (auto const &outer : area.outer_rings()) {
|
||||
auto &polygon = multipolygon.add_geometry();
|
||||
fill_polygon(&polygon, area, outer);
|
||||
}
|
||||
} else {
|
||||
auto &polygon = geom.set<polygon_t>();
|
||||
fill_polygon(&polygon, area, *area.outer_rings().begin());
|
||||
}
|
||||
|
||||
return geom;
|
||||
}
|
||||
|
||||
} // namespace geom
|
|
@ -0,0 +1,85 @@
|
|||
#ifndef OSM2PGSQL_GEOM_FROM_OSM_HPP
|
||||
#define OSM2PGSQL_GEOM_FROM_OSM_HPP
|
||||
|
||||
/**
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This file is part of osm2pgsql (https://osm2pgsql.org/).
|
||||
*
|
||||
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
|
||||
* For a full list of authors see the git log.
|
||||
*/
|
||||
|
||||
#include "geom.hpp"
|
||||
|
||||
#include <osmium/fwd.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Functions to create geometries from OSM data.
|
||||
*/
|
||||
|
||||
namespace geom {
|
||||
|
||||
/**
|
||||
* Create a point geometry from a node.
|
||||
*
|
||||
* \param node The input node.
|
||||
* \returns The created geometry.
|
||||
*/
|
||||
geometry_t create_point(osmium::Node const &node);
|
||||
|
||||
/**
|
||||
* Create a linestring geometry from a way. Nodes without location are ignored.
|
||||
* Consecutive nodes with the same location will only end up once in the
|
||||
* linestring.
|
||||
*
|
||||
* If the resulting linestring would be invalid (< 1 nodes), a null geometry
|
||||
* is returned.
|
||||
*
|
||||
* \param way The input way.
|
||||
* \returns The created geometry.
|
||||
*/
|
||||
geometry_t create_linestring(osmium::Way const &way);
|
||||
|
||||
/**
|
||||
* Create a polygon geometry from a way.
|
||||
*
|
||||
* If the resulting polygon would be invalid, a null geometry is returned.
|
||||
*
|
||||
* \param way The input way.
|
||||
* \returns The created geometry.
|
||||
*/
|
||||
geometry_t create_polygon(osmium::Way const &way);
|
||||
|
||||
/**
|
||||
* Create a multilinestring geometry from a bunch of ways (usually this
|
||||
* would be used for member ways of a relation). The result is always a
|
||||
* multilinestring, even if it only contains one linestring.
|
||||
*
|
||||
* If the resulting multilinestring would be invalid, a null geometry is
|
||||
* returned.
|
||||
*
|
||||
* \param ways Buffer containing all the input ways.
|
||||
* \returns The created geometry.
|
||||
*/
|
||||
geometry_t create_multilinestring(osmium::memory::Buffer const &ways);
|
||||
|
||||
/**
|
||||
* Create a (multi)polygon geometry from a relation and member ways.
|
||||
*
|
||||
* If the resulting (multi)polygon would be invalid, a null geometry is
|
||||
* returned.
|
||||
*
|
||||
* \param relation The input relation.
|
||||
* \param way_buffer Buffer containing all member ways.
|
||||
* \returns The created geometry.
|
||||
*/
|
||||
geometry_t create_multipolygon(osmium::Relation const &relation,
|
||||
osmium::memory::Buffer const &way_buffer);
|
||||
|
||||
} // namespace geom
|
||||
|
||||
#endif // OSM2PGSQL_GEOM_FROM_OSM_HPP
|
|
@ -0,0 +1,522 @@
|
|||
/**
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This file is part of osm2pgsql (https://osm2pgsql.org/).
|
||||
*
|
||||
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
|
||||
* For a full list of authors see the git log.
|
||||
*/
|
||||
|
||||
#include "geom-functions.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace geom {
|
||||
|
||||
double distance(point_t p1, point_t p2) noexcept
|
||||
{
|
||||
double const dx = p1.x() - p2.x();
|
||||
double const dy = p1.y() - p2.y();
|
||||
return std::sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
point_t interpolate(point_t p1, point_t p2, double frac) noexcept
|
||||
{
|
||||
return point_t{frac * (p1.x() - p2.x()) + p2.x(),
|
||||
frac * (p1.y() - p2.y()) + p2.y()};
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class transform_visitor
|
||||
{
|
||||
public:
|
||||
explicit transform_visitor(reprojection const *reprojection)
|
||||
: m_reprojection(reprojection)
|
||||
{}
|
||||
|
||||
geometry_t operator()(geom::nullgeom_t const & /*geom*/) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
geometry_t operator()(geom::point_t const &geom) const
|
||||
{
|
||||
return geometry_t{project(geom), srid()};
|
||||
}
|
||||
|
||||
geometry_t operator()(geom::linestring_t const &geom) const
|
||||
{
|
||||
geometry_t output{linestring_t{}, srid()};
|
||||
transform_points(&output.get<linestring_t>(), geom);
|
||||
return output;
|
||||
}
|
||||
|
||||
geometry_t operator()(geom::polygon_t const &geom) const
|
||||
{
|
||||
geometry_t output{polygon_t{}, srid()};
|
||||
transform_polygon(&output.get<polygon_t>(), geom);
|
||||
return output;
|
||||
}
|
||||
|
||||
geometry_t operator()(geom::multipoint_t const & /*geom*/) const
|
||||
{
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
geometry_t operator()(geom::multilinestring_t const &geom) const
|
||||
{
|
||||
geometry_t output{multilinestring_t{}, srid()};
|
||||
|
||||
for (auto const &line : geom) {
|
||||
transform_points(&output.get<multilinestring_t>().add_geometry(),
|
||||
line);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
geometry_t operator()(geom::multipolygon_t const &geom) const
|
||||
{
|
||||
geometry_t output{multipolygon_t{}, srid()};
|
||||
|
||||
for (auto const &polygon : geom) {
|
||||
transform_polygon(&output.get<multipolygon_t>().add_geometry(),
|
||||
polygon);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
geometry_t operator()(geom::collection_t const & /*geom*/) const
|
||||
{
|
||||
return {}; // XXX not implemented
|
||||
}
|
||||
|
||||
private:
|
||||
int srid() const noexcept { return m_reprojection->target_srs(); }
|
||||
|
||||
point_t project(point_t point) const
|
||||
{
|
||||
return m_reprojection->reproject(point);
|
||||
}
|
||||
|
||||
void transform_points(point_list_t *output, point_list_t const &input) const
|
||||
{
|
||||
output->reserve(input.size());
|
||||
for (auto const &point : input) {
|
||||
output->push_back(project(point));
|
||||
}
|
||||
}
|
||||
|
||||
void transform_polygon(polygon_t *output, polygon_t const &input) const
|
||||
{
|
||||
transform_points(&output->outer(), input.outer());
|
||||
|
||||
output->inners().reserve(input.inners().size());
|
||||
for (auto const &inner : input.inners()) {
|
||||
auto &oring = output->inners().emplace_back();
|
||||
transform_points(&oring, inner);
|
||||
}
|
||||
}
|
||||
|
||||
reprojection const *m_reprojection;
|
||||
|
||||
}; // class transform_visitor
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
geometry_t transform(geometry_t const &geom, reprojection const &reprojection)
|
||||
{
|
||||
assert(geom.srid() == 4326);
|
||||
return geom.visit(transform_visitor{&reprojection});
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Helper class for iterating over all points except the first one in a point
|
||||
* list.
|
||||
*/
|
||||
class without_first
|
||||
{
|
||||
public:
|
||||
explicit without_first(point_list_t const &list) : m_list(list) {}
|
||||
|
||||
point_list_t::const_iterator begin()
|
||||
{
|
||||
assert(m_list.begin() != m_list.end());
|
||||
return std::next(m_list.begin());
|
||||
}
|
||||
|
||||
point_list_t::const_iterator end() { return m_list.end(); }
|
||||
|
||||
private:
|
||||
point_list_t const &m_list;
|
||||
}; // class without_first
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
static void split_linestring(linestring_t const &line, double split_at,
|
||||
multilinestring_t *output)
|
||||
{
|
||||
double dist = 0;
|
||||
point_t prev_pt{line.front()};
|
||||
linestring_t *out = &output->add_geometry();
|
||||
out->push_back(prev_pt);
|
||||
|
||||
for (auto const &this_pt : without_first(line)) {
|
||||
double const delta = distance(prev_pt, this_pt);
|
||||
|
||||
// figure out if the addition of this point would take the total
|
||||
// length of the line in `segment` over the `split_at` distance.
|
||||
|
||||
if (dist + delta > split_at) {
|
||||
auto const splits = (size_t)std::floor((dist + delta) / split_at);
|
||||
// use the splitting distance to split the current segment up
|
||||
// into as many parts as necessary to keep each part below
|
||||
// the `split_at` distance.
|
||||
point_t ipoint;
|
||||
for (size_t j = 0; j < splits; ++j) {
|
||||
double const frac = ((double)(j + 1) * split_at - dist) / delta;
|
||||
ipoint = interpolate(this_pt, prev_pt, frac);
|
||||
if (frac != 0.0) {
|
||||
out->emplace_back(ipoint);
|
||||
}
|
||||
// start a new segment
|
||||
out = &output->add_geometry();
|
||||
out->emplace_back(ipoint);
|
||||
}
|
||||
// reset the distance based on the final splitting point for
|
||||
// the next iteration.
|
||||
if (this_pt == ipoint) {
|
||||
dist = 0;
|
||||
prev_pt = this_pt;
|
||||
continue;
|
||||
}
|
||||
dist = distance(this_pt, ipoint);
|
||||
} else {
|
||||
dist += delta;
|
||||
}
|
||||
|
||||
out->push_back(this_pt);
|
||||
|
||||
prev_pt = this_pt;
|
||||
}
|
||||
|
||||
if (out->size() <= 1) {
|
||||
output->resize(output->num_geometries() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
geometry_t segmentize(geometry_t const &geom, double max_segment_length)
|
||||
{
|
||||
geometry_t output{multilinestring_t{}, geom.srid()};
|
||||
auto *multilinestring = &output.get<multilinestring_t>();
|
||||
|
||||
if (geom.is_linestring()) {
|
||||
split_linestring(geom.get<linestring_t>(), max_segment_length,
|
||||
multilinestring);
|
||||
} else if (geom.is_multilinestring()) {
|
||||
for (auto const &line : geom.get<multilinestring_t>()) {
|
||||
split_linestring(line, max_segment_length, multilinestring);
|
||||
}
|
||||
} else {
|
||||
output.reset();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static double get_ring_area(ring_t const &ring) noexcept
|
||||
{
|
||||
assert(ring.size() > 3);
|
||||
|
||||
double total = 0.0;
|
||||
auto it = ring.begin();
|
||||
auto prev = *it++;
|
||||
|
||||
while (it != ring.end()) {
|
||||
auto const cur = *it;
|
||||
total += prev.x() * cur.y() - cur.x() * prev.y();
|
||||
prev = cur;
|
||||
++it;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static double get_polygon_area(polygon_t const &polygon)
|
||||
{
|
||||
double total = get_ring_area(polygon.outer());
|
||||
|
||||
for (auto const &ring : polygon.inners()) {
|
||||
total += get_ring_area(ring);
|
||||
}
|
||||
|
||||
return total * 0.5;
|
||||
}
|
||||
|
||||
double area(geometry_t const &geom)
|
||||
{
|
||||
double total = 0.0;
|
||||
|
||||
if (geom.is_polygon()) {
|
||||
total = get_polygon_area(geom.get<polygon_t>());
|
||||
} else if (geom.is_multipolygon()) {
|
||||
for (auto const &polygon : geom.get<multipolygon_t>()) {
|
||||
total += get_polygon_area(polygon);
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class split_visitor
|
||||
{
|
||||
public:
|
||||
split_visitor(std::vector<geometry_t> *output, uint32_t srid) noexcept
|
||||
: m_output(output), m_srid(srid)
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
void operator()(T) const
|
||||
{}
|
||||
|
||||
void operator()(geom::collection_t const &geom) const
|
||||
{
|
||||
for (auto sgeom : geom) {
|
||||
m_output->push_back(std::move(sgeom));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void operator()(geom::multigeometry_t<T> const &geom) const
|
||||
{
|
||||
for (auto sgeom : geom) {
|
||||
m_output->emplace_back(std::move(sgeom), m_srid);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<geometry_t> *m_output;
|
||||
uint32_t m_srid;
|
||||
|
||||
}; // class split_visitor
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
std::vector<geometry_t> split_multi(geometry_t geom, bool split_multi)
|
||||
{
|
||||
std::vector<geometry_t> output;
|
||||
|
||||
if (split_multi && geom.is_multi()) {
|
||||
geom.visit(split_visitor{&output, static_cast<uint32_t>(geom.srid())});
|
||||
} else if (!geom.is_null()) {
|
||||
output.push_back(std::move(geom));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add points specified by iterators to the linestring. If linestring is not
|
||||
* empty, do not add the first point returned by *it.
|
||||
*/
|
||||
template <typename ITERATOR>
|
||||
static void add_nodes_to_linestring(linestring_t *linestring, ITERATOR it,
|
||||
ITERATOR end)
|
||||
{
|
||||
if (!linestring->empty()) {
|
||||
assert(it != end);
|
||||
++it;
|
||||
}
|
||||
|
||||
while (it != end) {
|
||||
linestring->push_back(*it);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
geometry_t line_merge(geometry_t geom)
|
||||
{
|
||||
geometry_t output{multilinestring_t{}, geom.srid()};
|
||||
|
||||
if (geom.is_null()) {
|
||||
output.reset();
|
||||
return output;
|
||||
}
|
||||
|
||||
assert(geom.is_multilinestring());
|
||||
|
||||
// Make a list of all endpoints...
|
||||
struct endpoint_t
|
||||
{
|
||||
point_t c;
|
||||
std::size_t n;
|
||||
bool is_front;
|
||||
|
||||
endpoint_t(point_t coords, std::size_t size, bool front) noexcept
|
||||
: c(coords), n(size), is_front(front)
|
||||
{}
|
||||
|
||||
bool operator==(endpoint_t const &rhs) const noexcept
|
||||
{
|
||||
return c == rhs.c;
|
||||
}
|
||||
|
||||
bool operator<(endpoint_t const &rhs) const noexcept
|
||||
{
|
||||
return std::tuple<double, double, std::size_t, bool>{c.x(), c.y(),
|
||||
n, is_front} <
|
||||
std::tuple<double, double, std::size_t, bool>{
|
||||
rhs.c.x(), rhs.c.y(), rhs.n, rhs.is_front};
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<endpoint_t> endpoints;
|
||||
|
||||
// ...and a list of connections.
|
||||
constexpr std::size_t const NOCONN = -1UL;
|
||||
|
||||
struct connection_t
|
||||
{
|
||||
std::size_t left = NOCONN;
|
||||
geom::linestring_t const *ls;
|
||||
std::size_t right = NOCONN;
|
||||
|
||||
explicit connection_t(geom::linestring_t const *l) noexcept : ls(l) {}
|
||||
};
|
||||
|
||||
std::vector<connection_t> conns;
|
||||
|
||||
// Initialize the two lists.
|
||||
for (auto const &line : geom.get<multilinestring_t>()) {
|
||||
endpoints.emplace_back(line.front(), conns.size(), true);
|
||||
endpoints.emplace_back(line.back(), conns.size(), false);
|
||||
conns.emplace_back(&line);
|
||||
}
|
||||
|
||||
std::sort(endpoints.begin(), endpoints.end());
|
||||
|
||||
// Now fill the connection list based on the sorted enpoints list.
|
||||
for (auto it = std::adjacent_find(endpoints.cbegin(), endpoints.cend());
|
||||
it != endpoints.cend();
|
||||
it = std::adjacent_find(it + 2, endpoints.cend())) {
|
||||
auto const previd = it->n;
|
||||
auto const ptid = std::next(it)->n;
|
||||
if (it->is_front) {
|
||||
conns[previd].left = ptid;
|
||||
} else {
|
||||
conns[previd].right = ptid;
|
||||
}
|
||||
if (std::next(it)->is_front) {
|
||||
conns[ptid].left = previd;
|
||||
} else {
|
||||
conns[ptid].right = previd;
|
||||
}
|
||||
}
|
||||
|
||||
auto &linestrings = output.get<multilinestring_t>();
|
||||
|
||||
// First find all open ends and use them as starting points to assemble
|
||||
// linestrings. Mark ways as "done" as we go.
|
||||
std::size_t done_ways = 0;
|
||||
std::size_t const todo_ways = conns.size();
|
||||
for (std::size_t i = 0; i < todo_ways; ++i) {
|
||||
if (!conns[i].ls ||
|
||||
(conns[i].left != NOCONN && conns[i].right != NOCONN)) {
|
||||
continue; // way already done or not the beginning of a segment
|
||||
}
|
||||
|
||||
linestring_t linestring;
|
||||
{
|
||||
std::size_t prev = NOCONN;
|
||||
std::size_t cur = i;
|
||||
|
||||
do {
|
||||
auto &conn = conns[cur];
|
||||
assert(conn.ls);
|
||||
auto const &nl = *conn.ls;
|
||||
bool const forward = conn.left == prev;
|
||||
prev = cur;
|
||||
// add line
|
||||
if (forward) {
|
||||
add_nodes_to_linestring(&linestring, nl.cbegin(),
|
||||
nl.cend());
|
||||
cur = conn.right;
|
||||
} else {
|
||||
add_nodes_to_linestring(&linestring, nl.crbegin(),
|
||||
nl.crend());
|
||||
cur = conn.left;
|
||||
}
|
||||
// mark line as done
|
||||
conns[prev].ls = nullptr;
|
||||
++done_ways;
|
||||
} while (cur != NOCONN);
|
||||
}
|
||||
|
||||
// found a line end
|
||||
linestrings.add_geometry(std::move(linestring));
|
||||
}
|
||||
|
||||
// If all ways have been "done", i.e. are part of a linestring now, we
|
||||
// are finished.
|
||||
if (done_ways < todo_ways) {
|
||||
// oh dear, there must be circular ways without an end
|
||||
// need to do the same shebang again
|
||||
for (std::size_t i = 0; i < todo_ways; ++i) {
|
||||
if (!conns[i].ls) {
|
||||
continue; // way already done
|
||||
}
|
||||
|
||||
linestring_t linestring;
|
||||
{
|
||||
std::size_t prev = conns[i].left;
|
||||
std::size_t cur = i;
|
||||
|
||||
do {
|
||||
auto &conn = conns[cur];
|
||||
assert(conn.ls);
|
||||
auto const &nl = *conn.ls;
|
||||
bool const forward =
|
||||
(conn.left == prev &&
|
||||
(!conns[conn.left].ls ||
|
||||
conns[conn.left].ls->back() == nl.front()));
|
||||
prev = cur;
|
||||
if (forward) {
|
||||
// add line forwards
|
||||
add_nodes_to_linestring(&linestring, nl.cbegin(),
|
||||
nl.cend());
|
||||
cur = conn.right;
|
||||
} else {
|
||||
// add line backwards
|
||||
add_nodes_to_linestring(&linestring, nl.crbegin(),
|
||||
nl.crend());
|
||||
cur = conn.left;
|
||||
}
|
||||
// mark line as done
|
||||
conns[prev].ls = nullptr;
|
||||
} while (cur != i);
|
||||
}
|
||||
|
||||
// found a line end
|
||||
linestrings.add_geometry(std::move(linestring));
|
||||
}
|
||||
}
|
||||
|
||||
if (linestrings.num_geometries() == 0) {
|
||||
output.reset();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace geom
|
|
@ -0,0 +1,91 @@
|
|||
#ifndef OSM2PGSQL_GEOM_FUNCTIONS_HPP
|
||||
#define OSM2PGSQL_GEOM_FUNCTIONS_HPP
|
||||
|
||||
/**
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This file is part of osm2pgsql (https://osm2pgsql.org/).
|
||||
*
|
||||
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
|
||||
* For a full list of authors see the git log.
|
||||
*/
|
||||
|
||||
#include "geom.hpp"
|
||||
#include "reprojection.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Functions on geometries.
|
||||
*/
|
||||
|
||||
namespace geom {
|
||||
|
||||
/// Calculate the Euclidean distance between two points
|
||||
double distance(point_t p1, point_t p2) noexcept;
|
||||
|
||||
/**
|
||||
* Calculate a point on the vector p1 -> p2 that is a fraction of the distance
|
||||
* between those points.
|
||||
*/
|
||||
point_t interpolate(point_t p1, point_t p2, double frac) noexcept;
|
||||
|
||||
/**
|
||||
* Transform a geometry in 4326 into some other projection.
|
||||
*
|
||||
* \param geom Input geometry.
|
||||
* \param reprojection Target projection.
|
||||
* \returns Reprojected geometry.
|
||||
*
|
||||
* \pre \code geom.srid() == 4326 \endcode
|
||||
*/
|
||||
geometry_t transform(geometry_t const &geom, reprojection const &reprojection);
|
||||
|
||||
/**
|
||||
* Returns a modified geometry having no segment longer than the given
|
||||
* max_segment_length.
|
||||
*
|
||||
* \param line The input geometry, must be a linestring or multilinestring.
|
||||
* \param max_segment_length The maximum length (using Euclidean distance
|
||||
* in the length unit of the srs of the geometry) of each resulting
|
||||
* linestring.
|
||||
* \returns Resulting multilinestring geometry, nullgeom_t on error.
|
||||
*/
|
||||
geometry_t segmentize(geometry_t const &geom, double max_segment_length);
|
||||
|
||||
/**
|
||||
* Calculate area of geometry.
|
||||
* For geometry types other than polygon or multipolygon this will always
|
||||
* return 0.
|
||||
*
|
||||
* \param geom Input geometry.
|
||||
* \returns Area.
|
||||
*/
|
||||
double area(geometry_t const &geom);
|
||||
|
||||
/**
|
||||
* Split multigeometries into their parts. Non-multi geometries are left
|
||||
* alone and will end up as the only geometry in the result vector. If the
|
||||
* input geometry is a nullgeom_t, the result vector will be empty.
|
||||
*
|
||||
* \param geom Input geometry.
|
||||
* \param split_multi Only split of this is set to true.
|
||||
* \returns Vector of result geometries.
|
||||
*/
|
||||
std::vector<geometry_t> split_multi(geometry_t geom, bool split_multi = true);
|
||||
|
||||
/**
|
||||
* Merge lines in a multilinestring end-to-end as far as possible. Always
|
||||
* returns a multilinestring unless there is an error or the input geometry
|
||||
* is a nullgeom_t, in which case nullgeom_t is returned.
|
||||
*
|
||||
* \param geom Input geometry.
|
||||
* \returns Result multilinestring.
|
||||
*/
|
||||
geometry_t line_merge(geometry_t geom);
|
||||
|
||||
} // namespace geom
|
||||
|
||||
#endif // OSM2PGSQL_GEOM_FUNCTIONS_HPP
|
|
@ -7,6 +7,8 @@
|
|||
* For a full list of authors see the git log.
|
||||
*/
|
||||
|
||||
#include "geom-from-osm.hpp"
|
||||
#include "geom-functions.hpp"
|
||||
#include "geom-transform.hpp"
|
||||
#include "logging.hpp"
|
||||
|
||||
|
@ -22,14 +24,10 @@ bool geom_transform_point_t::is_compatible_with(
|
|||
geom_type == table_column_type::geometry;
|
||||
}
|
||||
|
||||
geom::osmium_builder_t::wkbs_t
|
||||
geom_transform_point_t::run(geom::osmium_builder_t *builder,
|
||||
table_column_type /*target_geom_type*/,
|
||||
osmium::Node const &node) const
|
||||
geom::geometry_t geom_transform_point_t::convert(reprojection const &proj,
|
||||
osmium::Node const &node) const
|
||||
{
|
||||
assert(builder);
|
||||
|
||||
return {builder->get_wkb_node(node.location())};
|
||||
return geom::transform(geom::create_point(node), proj);
|
||||
}
|
||||
|
||||
bool geom_transform_line_t::set_param(char const *name, lua_State *lua_state)
|
||||
|
@ -56,26 +54,27 @@ bool geom_transform_line_t::is_compatible_with(
|
|||
geom_type == table_column_type::geometry;
|
||||
}
|
||||
|
||||
geom::osmium_builder_t::wkbs_t
|
||||
geom_transform_line_t::run(geom::osmium_builder_t *builder,
|
||||
table_column_type /*target_geom_type*/,
|
||||
osmium::Way *way) const
|
||||
geom::geometry_t geom_transform_line_t::convert(reprojection const &proj,
|
||||
osmium::Way const &way) const
|
||||
{
|
||||
assert(builder);
|
||||
assert(way);
|
||||
|
||||
return builder->get_wkb_line(way->nodes(), m_split_at);
|
||||
auto geom = geom::transform(geom::create_linestring(way), proj);
|
||||
if (!geom.is_null() && m_split_at > 0.0) {
|
||||
geom = geom::segmentize(geom, m_split_at);
|
||||
}
|
||||
return geom;
|
||||
}
|
||||
|
||||
geom::osmium_builder_t::wkbs_t
|
||||
geom_transform_line_t::run(geom::osmium_builder_t *builder,
|
||||
table_column_type /*target_geom_type*/,
|
||||
osmium::Relation const & /*relation*/,
|
||||
osmium::memory::Buffer const &buffer) const
|
||||
geom::geometry_t
|
||||
geom_transform_line_t::convert(reprojection const &proj,
|
||||
osmium::Relation const & /*relation*/,
|
||||
osmium::memory::Buffer const &buffer) const
|
||||
{
|
||||
assert(builder);
|
||||
|
||||
return builder->get_wkb_multiline(buffer, m_split_at);
|
||||
auto geom = geom::transform(
|
||||
geom::line_merge(geom::create_multilinestring(buffer)), proj);
|
||||
if (!geom.is_null() && m_split_at > 0.0) {
|
||||
geom = geom::segmentize(geom, m_split_at);
|
||||
}
|
||||
return geom;
|
||||
}
|
||||
|
||||
bool geom_transform_area_t::set_param(char const *name, lua_State *lua_state)
|
||||
|
@ -115,42 +114,18 @@ bool geom_transform_area_t::is_compatible_with(
|
|||
geom_type == table_column_type::geometry;
|
||||
}
|
||||
|
||||
geom::osmium_builder_t::wkbs_t
|
||||
geom_transform_area_t::run(geom::osmium_builder_t *builder,
|
||||
table_column_type target_geom_type,
|
||||
osmium::Way *way) const
|
||||
geom::geometry_t geom_transform_area_t::convert(reprojection const & /*proj*/,
|
||||
osmium::Way const &way) const
|
||||
{
|
||||
assert(builder);
|
||||
assert(way);
|
||||
|
||||
geom::osmium_builder_t::wkbs_t result;
|
||||
|
||||
if (!way->is_closed()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.push_back(builder->get_wkb_polygon(*way));
|
||||
|
||||
if (result.front().empty()) {
|
||||
result.clear();
|
||||
} else if (target_geom_type == table_column_type::multipolygon) {
|
||||
builder->wrap_in_multipolygon(&result);
|
||||
}
|
||||
|
||||
return result;
|
||||
return geom::create_polygon(way);
|
||||
}
|
||||
|
||||
geom::osmium_builder_t::wkbs_t
|
||||
geom_transform_area_t::run(geom::osmium_builder_t *builder,
|
||||
table_column_type target_geom_type,
|
||||
osmium::Relation const &relation,
|
||||
osmium::memory::Buffer const &buffer) const
|
||||
geom::geometry_t
|
||||
geom_transform_area_t::convert(reprojection const & /*proj*/,
|
||||
osmium::Relation const &relation,
|
||||
osmium::memory::Buffer const &buffer) const
|
||||
{
|
||||
assert(builder);
|
||||
|
||||
bool const wrap_multi = target_geom_type == table_column_type::multipolygon;
|
||||
|
||||
return builder->get_wkb_multipolygon(relation, buffer, m_multi, wrap_multi);
|
||||
return geom::create_multipolygon(relation, buffer);
|
||||
}
|
||||
|
||||
std::unique_ptr<geom_transform_t> create_geom_transform(char const *type)
|
||||
|
|
|
@ -11,9 +11,11 @@
|
|||
*/
|
||||
|
||||
#include "flex-table-column.hpp"
|
||||
#include "osmium-builder.hpp"
|
||||
#include "geom.hpp"
|
||||
#include "reprojection.hpp"
|
||||
|
||||
#include <osmium/fwd.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
@ -36,44 +38,41 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
virtual bool is_compatible_with(table_column_type geom_type) const
|
||||
noexcept = 0;
|
||||
virtual bool
|
||||
is_compatible_with(table_column_type geom_type) const noexcept = 0;
|
||||
|
||||
virtual geom::osmium_builder_t::wkbs_t
|
||||
run(geom::osmium_builder_t * /*builder*/,
|
||||
table_column_type /*target_geom_type*/,
|
||||
osmium::Node const & /*node*/) const
|
||||
virtual geom::geometry_t convert(reprojection const & /*proj*/,
|
||||
osmium::Node const & /*node*/) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual geom::osmium_builder_t::wkbs_t
|
||||
run(geom::osmium_builder_t * /*builder*/,
|
||||
table_column_type /*target_geom_type*/, osmium::Way * /*way*/) const
|
||||
virtual geom::geometry_t convert(reprojection const & /*proj*/,
|
||||
osmium::Way const & /*way*/) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual geom::osmium_builder_t::wkbs_t
|
||||
run(geom::osmium_builder_t * /*builder*/,
|
||||
table_column_type /*target_geom_type*/,
|
||||
osmium::Relation const & /*relation*/,
|
||||
osmium::memory::Buffer const & /*buffer*/) const
|
||||
virtual geom::geometry_t
|
||||
convert(reprojection const & /*proj*/,
|
||||
osmium::Relation const & /*relation*/,
|
||||
osmium::memory::Buffer const & /*buffer*/) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual bool split() const noexcept { return false; }
|
||||
|
||||
}; // class geom_transform_t
|
||||
|
||||
class geom_transform_point_t : public geom_transform_t
|
||||
{
|
||||
public:
|
||||
bool is_compatible_with(table_column_type geom_type) const
|
||||
noexcept override;
|
||||
bool
|
||||
is_compatible_with(table_column_type geom_type) const noexcept override;
|
||||
|
||||
geom::osmium_builder_t::wkbs_t run(geom::osmium_builder_t *builder,
|
||||
table_column_type target_geom_type,
|
||||
osmium::Node const &node) const override;
|
||||
geom::geometry_t convert(reprojection const &proj,
|
||||
osmium::Node const &node) const override;
|
||||
|
||||
}; // class geom_transform_point_t
|
||||
|
||||
|
@ -82,17 +81,15 @@ class geom_transform_line_t : public geom_transform_t
|
|||
public:
|
||||
bool set_param(char const *name, lua_State *lua_state) override;
|
||||
|
||||
bool is_compatible_with(table_column_type geom_type) const
|
||||
noexcept override;
|
||||
bool
|
||||
is_compatible_with(table_column_type geom_type) const noexcept override;
|
||||
|
||||
geom::osmium_builder_t::wkbs_t run(geom::osmium_builder_t *builder,
|
||||
table_column_type target_geom_type,
|
||||
osmium::Way *way) const override;
|
||||
geom::geometry_t convert(reprojection const &proj,
|
||||
osmium::Way const &way) const override;
|
||||
|
||||
geom::osmium_builder_t::wkbs_t
|
||||
run(geom::osmium_builder_t *builder, table_column_type target_geom_type,
|
||||
osmium::Relation const &relation,
|
||||
osmium::memory::Buffer const &buffer) const override;
|
||||
geom::geometry_t
|
||||
convert(reprojection const &proj, osmium::Relation const &relation,
|
||||
osmium::memory::Buffer const &buffer) const override;
|
||||
|
||||
private:
|
||||
double m_split_at = 0.0;
|
||||
|
@ -104,17 +101,17 @@ class geom_transform_area_t : public geom_transform_t
|
|||
public:
|
||||
bool set_param(char const *name, lua_State *lua_state) override;
|
||||
|
||||
bool is_compatible_with(table_column_type geom_type) const
|
||||
noexcept override;
|
||||
bool
|
||||
is_compatible_with(table_column_type geom_type) const noexcept override;
|
||||
|
||||
geom::osmium_builder_t::wkbs_t run(geom::osmium_builder_t *builder,
|
||||
table_column_type target_geom_type,
|
||||
osmium::Way *way) const override;
|
||||
geom::geometry_t convert(reprojection const &proj,
|
||||
osmium::Way const &way) const override;
|
||||
|
||||
geom::osmium_builder_t::wkbs_t
|
||||
run(geom::osmium_builder_t *builder, table_column_type target_geom_type,
|
||||
osmium::Relation const &relation,
|
||||
osmium::memory::Buffer const &buffer) const override;
|
||||
geom::geometry_t
|
||||
convert(reprojection const &proj, osmium::Relation const &relation,
|
||||
osmium::memory::Buffer const &buffer) const override;
|
||||
|
||||
bool split() const noexcept override { return !m_multi; }
|
||||
|
||||
private:
|
||||
bool m_multi = true;
|
||||
|
|
277
src/geom.cpp
277
src/geom.cpp
|
@ -9,287 +9,18 @@
|
|||
|
||||
#include "geom.hpp"
|
||||
|
||||
#include "osmtypes.hpp"
|
||||
|
||||
#include <osmium/osm/way.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
|
||||
namespace geom {
|
||||
|
||||
double distance(osmium::geom::Coordinates p1,
|
||||
osmium::geom::Coordinates p2) noexcept
|
||||
bool operator==(point_list_t const &a, point_list_t const &b) noexcept
|
||||
{
|
||||
double const dx = p1.x - p2.x;
|
||||
double const dy = p1.y - p2.y;
|
||||
return std::sqrt(dx * dx + dy * dy);
|
||||
return std::equal(a.cbegin(), a.cend(), b.cbegin(), b.cend());
|
||||
}
|
||||
|
||||
osmium::geom::Coordinates interpolate(osmium::geom::Coordinates p1,
|
||||
osmium::geom::Coordinates p2,
|
||||
double frac) noexcept
|
||||
bool operator!=(point_list_t const &a, point_list_t const &b) noexcept
|
||||
{
|
||||
return osmium::geom::Coordinates{frac * (p1.x - p2.x) + p2.x,
|
||||
frac * (p1.y - p2.y) + p2.y};
|
||||
}
|
||||
|
||||
linestring_t::linestring_t(
|
||||
std::initializer_list<osmium::geom::Coordinates> coords)
|
||||
{
|
||||
std::copy(coords.begin(), coords.end(), std::back_inserter(m_coordinates));
|
||||
}
|
||||
|
||||
linestring_t::linestring_t(osmium::NodeRefList const &nodes,
|
||||
reprojection const &proj)
|
||||
{
|
||||
osmium::Location last{};
|
||||
for (auto const &node : nodes) {
|
||||
auto const loc = node.location();
|
||||
if (loc.valid() && loc != last) {
|
||||
add_point(proj.reproject(loc));
|
||||
last = loc;
|
||||
}
|
||||
}
|
||||
|
||||
if (size() <= 1) {
|
||||
m_coordinates.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void split_linestring(linestring_t const &line, double split_at,
|
||||
std::vector<linestring_t> *out)
|
||||
{
|
||||
double dist = 0;
|
||||
osmium::geom::Coordinates prev_pt{};
|
||||
out->emplace_back();
|
||||
|
||||
for (auto const &this_pt : line) {
|
||||
if (prev_pt.valid()) {
|
||||
double const delta = distance(prev_pt, this_pt);
|
||||
|
||||
// figure out if the addition of this point would take the total
|
||||
// length of the line in `segment` over the `split_at` distance.
|
||||
|
||||
if (dist + delta > split_at) {
|
||||
auto const splits =
|
||||
(size_t)std::floor((dist + delta) / split_at);
|
||||
// use the splitting distance to split the current segment up
|
||||
// into as many parts as necessary to keep each part below
|
||||
// the `split_at` distance.
|
||||
osmium::geom::Coordinates ipoint;
|
||||
for (size_t j = 0; j < splits; ++j) {
|
||||
double const frac =
|
||||
((double)(j + 1) * split_at - dist) / delta;
|
||||
ipoint = interpolate(this_pt, prev_pt, frac);
|
||||
if (frac != 0.0) {
|
||||
out->back().add_point(ipoint);
|
||||
}
|
||||
// start a new segment
|
||||
out->emplace_back();
|
||||
out->back().add_point(ipoint);
|
||||
}
|
||||
// reset the distance based on the final splitting point for
|
||||
// the next iteration.
|
||||
if (this_pt == ipoint) {
|
||||
dist = 0;
|
||||
prev_pt = this_pt;
|
||||
continue;
|
||||
}
|
||||
dist = distance(this_pt, ipoint);
|
||||
} else {
|
||||
dist += delta;
|
||||
}
|
||||
}
|
||||
|
||||
out->back().add_point(this_pt);
|
||||
|
||||
prev_pt = this_pt;
|
||||
}
|
||||
|
||||
if (out->back().size() <= 1) {
|
||||
out->pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void make_line(linestring_t &&line, double split_at,
|
||||
std::vector<linestring_t> *out)
|
||||
{
|
||||
assert(out);
|
||||
|
||||
if (line.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (split_at > 0.0) {
|
||||
split_linestring(line, split_at, out);
|
||||
} else {
|
||||
out->emplace_back(std::move(line));
|
||||
}
|
||||
}
|
||||
|
||||
void make_multiline(osmium::memory::Buffer const &ways, double split_at,
|
||||
reprojection const &proj, std::vector<linestring_t> *out)
|
||||
|
||||
{
|
||||
// make a list of all endpoints
|
||||
struct endpoint_t {
|
||||
osmid_t id;
|
||||
std::size_t n;
|
||||
bool is_front;
|
||||
|
||||
endpoint_t(osmid_t ref, std::size_t size, bool front) noexcept
|
||||
: id(ref), n(size), is_front(front)
|
||||
{}
|
||||
|
||||
bool operator==(endpoint_t const &rhs) const noexcept
|
||||
{
|
||||
return id == rhs.id;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<endpoint_t> endpoints;
|
||||
|
||||
// and a list of way connections
|
||||
enum lmt : size_t
|
||||
{
|
||||
NOCONN = -1UL
|
||||
};
|
||||
|
||||
struct connection_t {
|
||||
std::size_t left = NOCONN;
|
||||
osmium::Way const *way;
|
||||
std::size_t right = NOCONN;
|
||||
|
||||
explicit connection_t(osmium::Way const *w) noexcept : way(w) {}
|
||||
};
|
||||
|
||||
std::vector<connection_t> conns;
|
||||
|
||||
// initialise the two lists
|
||||
for (auto const &w : ways.select<osmium::Way>()) {
|
||||
if (w.nodes().size() > 1) {
|
||||
endpoints.emplace_back(w.nodes().front().ref(), conns.size(), true);
|
||||
endpoints.emplace_back(w.nodes().back().ref(), conns.size(), false);
|
||||
conns.emplace_back(&w);
|
||||
}
|
||||
}
|
||||
|
||||
// sort by node id
|
||||
std::sort(endpoints.begin(), endpoints.end(), [
|
||||
](endpoint_t const &a, endpoint_t const &b) noexcept {
|
||||
return std::tuple<osmid_t, std::size_t, bool>(a.id, a.n, a.is_front) <
|
||||
std::tuple<osmid_t, std::size_t, bool>(b.id, b.n, b.is_front);
|
||||
});
|
||||
|
||||
// now fill the connection list based on the sorted list
|
||||
for (auto it = std::adjacent_find(endpoints.cbegin(), endpoints.cend());
|
||||
it != endpoints.cend();
|
||||
it = std::adjacent_find(it + 2, endpoints.cend())) {
|
||||
auto const previd = it->n;
|
||||
auto const ptid = std::next(it)->n;
|
||||
if (it->is_front) {
|
||||
conns[previd].left = ptid;
|
||||
} else {
|
||||
conns[previd].right = ptid;
|
||||
}
|
||||
if (std::next(it)->is_front) {
|
||||
conns[ptid].left = previd;
|
||||
} else {
|
||||
conns[ptid].right = previd;
|
||||
}
|
||||
}
|
||||
|
||||
// First find all open ends and use them as starting points to assemble
|
||||
// linestrings. Mark ways as "done" as we go.
|
||||
std::size_t done_ways = 0;
|
||||
std::size_t const todo_ways = conns.size();
|
||||
for (std::size_t i = 0; i < todo_ways; ++i) {
|
||||
if (!conns[i].way ||
|
||||
(conns[i].left != NOCONN && conns[i].right != NOCONN)) {
|
||||
continue; // way already done or not the beginning of a segment
|
||||
}
|
||||
|
||||
linestring_t linestring;
|
||||
{
|
||||
std::size_t prev = NOCONN;
|
||||
std::size_t cur = i;
|
||||
|
||||
do {
|
||||
auto &conn = conns[cur];
|
||||
assert(conn.way);
|
||||
auto const &nl = conn.way->nodes();
|
||||
bool const forward = conn.left == prev;
|
||||
prev = cur;
|
||||
// add way nodes
|
||||
if (forward) {
|
||||
add_nodes_to_linestring(linestring, proj, nl.cbegin(),
|
||||
nl.cend());
|
||||
cur = conn.right;
|
||||
} else {
|
||||
add_nodes_to_linestring(linestring, proj, nl.crbegin(),
|
||||
nl.crend());
|
||||
cur = conn.left;
|
||||
}
|
||||
// mark way as done
|
||||
conns[prev].way = nullptr;
|
||||
++done_ways;
|
||||
} while (cur != NOCONN);
|
||||
}
|
||||
|
||||
// found a line end, create the wkbs
|
||||
make_line(std::move(linestring), split_at, out);
|
||||
}
|
||||
|
||||
// If all ways have been "done", i.e. are part of a linestring now, we
|
||||
// are finished.
|
||||
if (done_ways >= todo_ways) {
|
||||
return;
|
||||
}
|
||||
|
||||
// oh dear, there must be circular ways without an end
|
||||
// need to do the same shebang again
|
||||
for (size_t i = 0; i < todo_ways; ++i) {
|
||||
if (!conns[i].way) {
|
||||
continue; // way already done
|
||||
}
|
||||
|
||||
linestring_t linestring;
|
||||
{
|
||||
size_t prev = conns[i].left;
|
||||
size_t cur = i;
|
||||
|
||||
do {
|
||||
auto &conn = conns[cur];
|
||||
assert(conn.way);
|
||||
auto const &nl = conn.way->nodes();
|
||||
bool const forward =
|
||||
(conn.left == prev &&
|
||||
(!conns[conn.left].way ||
|
||||
conns[conn.left].way->nodes().back() == nl.front()));
|
||||
prev = cur;
|
||||
if (forward) {
|
||||
// add way forwards
|
||||
add_nodes_to_linestring(linestring, proj, nl.cbegin(),
|
||||
nl.cend());
|
||||
cur = conn.right;
|
||||
} else {
|
||||
// add way backwards
|
||||
add_nodes_to_linestring(linestring, proj, nl.crbegin(),
|
||||
nl.crend());
|
||||
cur = conn.left;
|
||||
}
|
||||
// mark way as done
|
||||
conns[prev].way = nullptr;
|
||||
} while (cur != i);
|
||||
}
|
||||
|
||||
// found a line end, create the wkbs
|
||||
make_line(std::move(linestring), split_at, out);
|
||||
}
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
} // namespace geom
|
||||
|
||||
|
|
323
src/geom.hpp
323
src/geom.hpp
|
@ -13,168 +13,229 @@
|
|||
/**
|
||||
* \file
|
||||
*
|
||||
* Low level geometry functions and types.
|
||||
* Basic geometry types and functions.
|
||||
*/
|
||||
|
||||
#include "reprojection.hpp"
|
||||
#include <osmium/osm/location.hpp>
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/osm/node_ref_list.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <initializer_list>
|
||||
#include <ostream>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace geom {
|
||||
|
||||
/// Calculate the Euclidean distance between two coordinates
|
||||
double distance(osmium::geom::Coordinates p1,
|
||||
osmium::geom::Coordinates p2) noexcept;
|
||||
class nullgeom_t
|
||||
{
|
||||
}; // class nullgeom_t
|
||||
|
||||
osmium::geom::Coordinates interpolate(osmium::geom::Coordinates p1,
|
||||
osmium::geom::Coordinates p2,
|
||||
double frac) noexcept;
|
||||
class point_t
|
||||
{
|
||||
public:
|
||||
point_t() = default;
|
||||
|
||||
class linestring_t
|
||||
explicit point_t(osmium::Location location) noexcept
|
||||
: m_x(location.lon_without_check()), m_y(location.lat_without_check())
|
||||
{}
|
||||
|
||||
constexpr point_t(double x, double y) noexcept : m_x(x), m_y(y) {}
|
||||
|
||||
constexpr double x() const noexcept { return m_x; }
|
||||
constexpr double y() const noexcept { return m_y; }
|
||||
|
||||
constexpr void set_x(double value) noexcept { m_x = value; }
|
||||
constexpr void set_y(double value) noexcept { m_y = value; }
|
||||
|
||||
constexpr friend bool operator==(point_t a, point_t b) noexcept
|
||||
{
|
||||
return a.x() == b.x() && a.y() == b.y();
|
||||
}
|
||||
|
||||
constexpr friend bool operator!=(point_t a, point_t b) noexcept
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
private:
|
||||
double m_x = 0.0;
|
||||
double m_y = 0.0;
|
||||
|
||||
}; // class point_t
|
||||
|
||||
/// This type is used as the basis for linestrings and rings.
|
||||
using point_list_t = std::vector<point_t>;
|
||||
|
||||
bool operator==(point_list_t const &a, point_list_t const &b) noexcept;
|
||||
bool operator!=(point_list_t const &a, point_list_t const &b) noexcept;
|
||||
|
||||
class linestring_t : public point_list_t
|
||||
{
|
||||
public:
|
||||
linestring_t() = default;
|
||||
|
||||
linestring_t(std::initializer_list<osmium::geom::Coordinates> coords);
|
||||
template <typename Iterator>
|
||||
linestring_t(Iterator begin, Iterator end) : point_list_t(begin, end)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Construct a linestring from a way node list. Nodes without location
|
||||
* are ignored. Consecutive nodes with the same location will only end
|
||||
* up once in the linestring.
|
||||
*
|
||||
* The resulting linestring will either be empty or have at least two
|
||||
* points in it.
|
||||
*
|
||||
* \param nodes The input nodes.
|
||||
* \param proj The projection used to project all coordinates.
|
||||
*/
|
||||
linestring_t(osmium::NodeRefList const &nodes, reprojection const &proj);
|
||||
|
||||
using iterator = std::vector<osmium::geom::Coordinates>::iterator;
|
||||
|
||||
using const_iterator =
|
||||
std::vector<osmium::geom::Coordinates>::const_iterator;
|
||||
|
||||
bool empty() const noexcept { return m_coordinates.empty(); }
|
||||
|
||||
std::size_t size() const noexcept { return m_coordinates.size(); }
|
||||
|
||||
void clear() noexcept { m_coordinates.clear(); }
|
||||
|
||||
void add_point(osmium::geom::Coordinates coordinates)
|
||||
{
|
||||
m_coordinates.emplace_back(coordinates);
|
||||
}
|
||||
|
||||
iterator begin() noexcept { return m_coordinates.begin(); }
|
||||
|
||||
iterator end() noexcept { return m_coordinates.end(); }
|
||||
|
||||
const_iterator begin() const noexcept { return m_coordinates.cbegin(); }
|
||||
|
||||
const_iterator end() const noexcept { return m_coordinates.cend(); }
|
||||
|
||||
const_iterator cbegin() const noexcept { return m_coordinates.cbegin(); }
|
||||
|
||||
const_iterator cend() const noexcept { return m_coordinates.cend(); }
|
||||
|
||||
osmium::geom::Coordinates &operator[](std::size_t n) noexcept
|
||||
{
|
||||
return m_coordinates[n];
|
||||
}
|
||||
|
||||
osmium::geom::Coordinates operator[](std::size_t n) const noexcept
|
||||
{
|
||||
return m_coordinates[n];
|
||||
}
|
||||
|
||||
friend bool operator==(linestring_t const &a,
|
||||
linestring_t const &b) noexcept
|
||||
{
|
||||
if (a.size() != b.size()) {
|
||||
return false;
|
||||
}
|
||||
return std::equal(a.cbegin(), a.cend(), b.cbegin());
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<osmium::geom::Coordinates> m_coordinates;
|
||||
linestring_t(std::initializer_list<point_t> list)
|
||||
: point_list_t(list.begin(), list.end())
|
||||
{}
|
||||
|
||||
}; // class linestring_t
|
||||
|
||||
/// Output a linestring in WKT format. Used for debugging.
|
||||
template <typename TChar, typename TTraits>
|
||||
std::basic_ostream<TChar, TTraits> &
|
||||
operator<<(std::basic_ostream<TChar, TTraits> &out, const linestring_t &line)
|
||||
class ring_t : public point_list_t
|
||||
{
|
||||
out << "LINESTRING(";
|
||||
public:
|
||||
ring_t() = default;
|
||||
|
||||
bool first = true;
|
||||
for (auto const &coord : line) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
out << ',';
|
||||
}
|
||||
out << coord.x << ' ' << coord.y;
|
||||
}
|
||||
template <typename Iterator>
|
||||
ring_t(Iterator begin, Iterator end) : point_list_t(begin, end)
|
||||
{}
|
||||
|
||||
return out << ')';
|
||||
}
|
||||
ring_t(std::initializer_list<point_t> list)
|
||||
: point_list_t(list.begin(), list.end())
|
||||
{}
|
||||
|
||||
}; // class ring_t
|
||||
|
||||
class polygon_t
|
||||
{
|
||||
public:
|
||||
polygon_t() = default;
|
||||
|
||||
explicit polygon_t(ring_t &&ring) : m_outer(std::move(ring)) {}
|
||||
|
||||
ring_t const &outer() const noexcept { return m_outer; }
|
||||
|
||||
ring_t &outer() noexcept { return m_outer; }
|
||||
|
||||
std::vector<ring_t> const &inners() const noexcept { return m_inners; }
|
||||
|
||||
std::vector<ring_t> &inners() noexcept { return m_inners; }
|
||||
|
||||
void add_inner_ring(ring_t &&ring) { m_inners.push_back(std::move(ring)); }
|
||||
|
||||
private:
|
||||
ring_t m_outer;
|
||||
std::vector<ring_t> m_inners;
|
||||
|
||||
}; // class polygon_t
|
||||
|
||||
template <typename GEOM>
|
||||
class multigeometry_t : public std::vector<GEOM>
|
||||
{
|
||||
public:
|
||||
using const_iterator = typename std::vector<GEOM>::const_iterator;
|
||||
|
||||
std::size_t num_geometries() const noexcept { return this->size(); }
|
||||
|
||||
void add_geometry(GEOM &&geom) { this->push_back(std::move(geom)); }
|
||||
|
||||
GEOM &add_geometry() { return this->emplace_back(); }
|
||||
|
||||
}; // class multigeometry_t
|
||||
|
||||
using multipoint_t = multigeometry_t<point_t>;
|
||||
using multilinestring_t = multigeometry_t<linestring_t>;
|
||||
using multipolygon_t = multigeometry_t<polygon_t>;
|
||||
|
||||
class geometry_t;
|
||||
|
||||
using collection_t = multigeometry_t<geometry_t>;
|
||||
|
||||
/**
|
||||
* Possibly split linestring into several linestrings making sure each one
|
||||
* is no longer than split_at.
|
||||
*
|
||||
* \param line The input line string.
|
||||
* \param split_at The maximum length (using Euclidean distance) of each
|
||||
* resulting linestring.
|
||||
* \param out Add resulting linestrings to this vector.
|
||||
* This is a variant type holding any one of the geometry types (including
|
||||
* nullgeom_t) and a SRID.
|
||||
*/
|
||||
void split_linestring(linestring_t const &line, double split_at,
|
||||
std::vector<linestring_t> *out);
|
||||
|
||||
void make_line(linestring_t &&line, double split_at,
|
||||
std::vector<linestring_t> *out);
|
||||
|
||||
/**
|
||||
* Add nodes specified by iterators to the linestring projecting them in the
|
||||
* process. If linestring is not empty, do not add the first node returned
|
||||
* by *begin.
|
||||
*/
|
||||
template <typename ITERATOR>
|
||||
void add_nodes_to_linestring(geom::linestring_t &linestring,
|
||||
reprojection const &proj, ITERATOR const &begin,
|
||||
ITERATOR const &end)
|
||||
class geometry_t
|
||||
{
|
||||
auto it = begin;
|
||||
if (!linestring.empty()) {
|
||||
assert(it != end);
|
||||
++it;
|
||||
public:
|
||||
constexpr geometry_t() = default;
|
||||
|
||||
template <typename T>
|
||||
constexpr explicit geometry_t(T geom, int srid = 4326)
|
||||
: m_geom(std::move(geom)), m_srid(srid)
|
||||
{}
|
||||
|
||||
constexpr int srid() const noexcept { return m_srid; }
|
||||
|
||||
constexpr void set_srid(int srid) noexcept { m_srid = srid; }
|
||||
|
||||
constexpr bool is_null() const noexcept
|
||||
{
|
||||
return std::holds_alternative<nullgeom_t>(m_geom);
|
||||
}
|
||||
constexpr bool is_point() const noexcept
|
||||
{
|
||||
return std::holds_alternative<point_t>(m_geom);
|
||||
}
|
||||
constexpr bool is_linestring() const noexcept
|
||||
{
|
||||
return std::holds_alternative<linestring_t>(m_geom);
|
||||
}
|
||||
constexpr bool is_polygon() const noexcept
|
||||
{
|
||||
return std::holds_alternative<polygon_t>(m_geom);
|
||||
}
|
||||
constexpr bool is_multipoint() const noexcept
|
||||
{
|
||||
return std::holds_alternative<multipoint_t>(m_geom);
|
||||
}
|
||||
constexpr bool is_multilinestring() const noexcept
|
||||
{
|
||||
return std::holds_alternative<multilinestring_t>(m_geom);
|
||||
}
|
||||
constexpr bool is_multipolygon() const noexcept
|
||||
{
|
||||
return std::holds_alternative<multipolygon_t>(m_geom);
|
||||
}
|
||||
constexpr bool is_collection() const noexcept
|
||||
{
|
||||
return std::holds_alternative<collection_t>(m_geom);
|
||||
}
|
||||
|
||||
osmium::Location last{};
|
||||
while (it != end) {
|
||||
auto const loc = it->location();
|
||||
if (loc.valid() && loc != last) {
|
||||
linestring.add_point(proj.reproject(loc));
|
||||
last = loc;
|
||||
}
|
||||
++it;
|
||||
constexpr bool is_multi() const noexcept
|
||||
{
|
||||
return is_multipoint() || is_multilinestring() || is_multipolygon() ||
|
||||
is_collection();
|
||||
}
|
||||
}
|
||||
|
||||
void make_multiline(osmium::memory::Buffer const &ways, double split_at,
|
||||
reprojection const &proj, std::vector<linestring_t> *out);
|
||||
template <typename T>
|
||||
constexpr T const &get() const
|
||||
{
|
||||
return std::get<T>(m_geom);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T &get()
|
||||
{
|
||||
return std::get<T>(m_geom);
|
||||
}
|
||||
|
||||
void reset() { m_geom.emplace<nullgeom_t>(); }
|
||||
|
||||
template <typename T>
|
||||
constexpr T &set()
|
||||
{
|
||||
return m_geom.emplace<T>();
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr auto visit(V &&visitor) const
|
||||
{
|
||||
return std::visit(std::forward<V>(visitor), m_geom);
|
||||
}
|
||||
|
||||
private:
|
||||
std::variant<nullgeom_t, point_t, linestring_t, polygon_t, multipoint_t,
|
||||
multilinestring_t, multipolygon_t, collection_t>
|
||||
m_geom = nullgeom_t{};
|
||||
|
||||
int m_srid = 4326;
|
||||
|
||||
}; // class geometry_t
|
||||
|
||||
} // namespace geom
|
||||
|
||||
|
|
|
@ -1,204 +0,0 @@
|
|||
/**
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This file is part of osm2pgsql (https://osm2pgsql.org/).
|
||||
*
|
||||
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
|
||||
* For a full list of authors see the git log.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/area/geom_assembler.hpp>
|
||||
|
||||
#include "geom.hpp"
|
||||
#include "osmium-builder.hpp"
|
||||
|
||||
namespace geom {
|
||||
|
||||
void osmium_builder_t::wrap_in_multipolygon(
|
||||
osmium_builder_t::wkbs_t *geometries)
|
||||
{
|
||||
assert(!geometries->empty());
|
||||
|
||||
m_writer.multipolygon_start();
|
||||
for (auto const &p : *geometries) {
|
||||
m_writer.add_sub_geometry(p);
|
||||
}
|
||||
(*geometries)[0] = m_writer.multipolygon_finish(geometries->size());
|
||||
geometries->resize(1);
|
||||
}
|
||||
|
||||
void osmium_builder_t::wrap_in_multipolygon(osmium_builder_t::wkb_t *geometry)
|
||||
{
|
||||
m_writer.multipolygon_start();
|
||||
m_writer.add_sub_geometry(*geometry);
|
||||
*geometry = m_writer.multipolygon_finish(1);
|
||||
}
|
||||
|
||||
osmium_builder_t::wkb_t
|
||||
osmium_builder_t::get_wkb_node(osmium::Location const &loc) const
|
||||
{
|
||||
return m_writer.make_point(m_proj->reproject(loc));
|
||||
}
|
||||
|
||||
osmium_builder_t::wkbs_t
|
||||
osmium_builder_t::get_wkb_line(osmium::WayNodeList const &nodes,
|
||||
double split_at)
|
||||
{
|
||||
std::vector<linestring_t> linestrings;
|
||||
geom::make_line(linestring_t{nodes, *m_proj}, split_at, &linestrings);
|
||||
|
||||
wkbs_t ret;
|
||||
|
||||
for (auto const &line : linestrings) {
|
||||
m_writer.linestring_start();
|
||||
for (auto const &coord : line) {
|
||||
m_writer.add_location(coord);
|
||||
}
|
||||
ret.push_back(m_writer.linestring_finish(line.size()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
osmium_builder_t::wkb_t
|
||||
osmium_builder_t::get_wkb_polygon(osmium::Way const &way)
|
||||
{
|
||||
osmium::area::AssemblerConfig area_config;
|
||||
area_config.ignore_invalid_locations = true;
|
||||
osmium::area::GeomAssembler assembler{area_config};
|
||||
|
||||
m_buffer.clear();
|
||||
if (!assembler(way, m_buffer)) {
|
||||
return wkb_t();
|
||||
}
|
||||
|
||||
auto const wkbs = create_polygons(m_buffer.get<osmium::Area>(0));
|
||||
|
||||
return wkbs.empty() ? wkb_t{} : wkbs[0];
|
||||
}
|
||||
|
||||
osmium_builder_t::wkbs_t
|
||||
osmium_builder_t::get_wkb_multipolygon(osmium::Relation const &rel,
|
||||
osmium::memory::Buffer const &ways,
|
||||
bool build_multigeoms, bool wrap_multi)
|
||||
{
|
||||
osmium::area::AssemblerConfig area_config;
|
||||
area_config.ignore_invalid_locations = true;
|
||||
osmium::area::GeomAssembler assembler{area_config};
|
||||
|
||||
m_buffer.clear();
|
||||
|
||||
wkbs_t ret;
|
||||
if (assembler(rel, ways, m_buffer)) {
|
||||
auto const &area = m_buffer.get<osmium::Area>(0);
|
||||
|
||||
// This returns a vector of one or more polygons
|
||||
ret = create_polygons(area);
|
||||
assert(!ret.empty());
|
||||
|
||||
if (build_multigeoms) {
|
||||
if (ret.size() > 1 || wrap_multi) {
|
||||
wrap_in_multipolygon(&ret);
|
||||
}
|
||||
} else {
|
||||
if (wrap_multi) {
|
||||
for (auto &wkb : ret) {
|
||||
// wrap each polygon into its own multipolygon
|
||||
wrap_in_multipolygon(&wkb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
osmium_builder_t::wkbs_t
|
||||
osmium_builder_t::get_wkb_multiline(osmium::memory::Buffer const &ways,
|
||||
double split_at)
|
||||
{
|
||||
std::vector<linestring_t> linestrings;
|
||||
make_multiline(ways, split_at, *m_proj, &linestrings);
|
||||
|
||||
wkbs_t ret;
|
||||
|
||||
for (auto const &line : linestrings) {
|
||||
m_writer.linestring_start();
|
||||
for (auto const &coord : line) {
|
||||
m_writer.add_location(coord);
|
||||
}
|
||||
ret.push_back(m_writer.linestring_finish(line.size()));
|
||||
}
|
||||
|
||||
if (split_at <= 0.0 && !ret.empty()) {
|
||||
auto const num_lines = ret.size();
|
||||
m_writer.multilinestring_start();
|
||||
for (auto const &line : ret) {
|
||||
m_writer.add_sub_geometry(line);
|
||||
}
|
||||
ret.clear();
|
||||
ret.push_back(m_writer.multilinestring_finish(num_lines));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t osmium_builder_t::add_mp_points(osmium::NodeRefList const &nodes)
|
||||
{
|
||||
size_t num_points = 0;
|
||||
osmium::Location last_location;
|
||||
for (auto const &node_ref : nodes) {
|
||||
if (node_ref.location().valid() &&
|
||||
last_location != node_ref.location()) {
|
||||
last_location = node_ref.location();
|
||||
m_writer.add_location(m_proj->reproject(last_location));
|
||||
++num_points;
|
||||
}
|
||||
}
|
||||
|
||||
return num_points;
|
||||
}
|
||||
|
||||
osmium_builder_t::wkbs_t
|
||||
osmium_builder_t::create_polygons(osmium::Area const &area)
|
||||
{
|
||||
wkbs_t ret;
|
||||
|
||||
try {
|
||||
size_t num_rings = 0;
|
||||
|
||||
for (auto const &item : area) {
|
||||
if (item.type() == osmium::item_type::outer_ring) {
|
||||
auto const &ring = static_cast<osmium::OuterRing const &>(item);
|
||||
if (num_rings > 0) {
|
||||
ret.push_back(m_writer.polygon_finish(num_rings));
|
||||
num_rings = 0;
|
||||
}
|
||||
m_writer.polygon_start();
|
||||
m_writer.polygon_ring_start();
|
||||
auto const num_points = add_mp_points(ring);
|
||||
m_writer.polygon_ring_finish(num_points);
|
||||
++num_rings;
|
||||
} else if (item.type() == osmium::item_type::inner_ring) {
|
||||
auto const &ring = static_cast<osmium::InnerRing const &>(item);
|
||||
m_writer.polygon_ring_start();
|
||||
auto const num_points = add_mp_points(ring);
|
||||
m_writer.polygon_ring_finish(num_points);
|
||||
++num_rings;
|
||||
}
|
||||
}
|
||||
|
||||
auto const wkb = m_writer.polygon_finish(num_rings);
|
||||
if (num_rings > 0) {
|
||||
ret.push_back(wkb);
|
||||
}
|
||||
|
||||
} catch (osmium::geometry_error const &) { /* ignored */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace geom
|
|
@ -1,71 +0,0 @@
|
|||
#ifndef OSM2PGSQL_OSMIUM_BUILDER_HPP
|
||||
#define OSM2PGSQL_OSMIUM_BUILDER_HPP
|
||||
|
||||
/**
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This file is part of osm2pgsql (https://osm2pgsql.org/).
|
||||
*
|
||||
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
|
||||
* For a full list of authors see the git log.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm.hpp>
|
||||
|
||||
#include "reprojection.hpp"
|
||||
#include "wkb.hpp"
|
||||
|
||||
namespace geom {
|
||||
|
||||
class osmium_builder_t
|
||||
{
|
||||
public:
|
||||
using wkb_t = std::string;
|
||||
using wkbs_t = std::vector<std::string>;
|
||||
|
||||
explicit osmium_builder_t(std::shared_ptr<reprojection> const &proj)
|
||||
: m_proj(proj), m_buffer(1024, osmium::memory::Buffer::auto_grow::yes),
|
||||
m_writer(m_proj->target_srs())
|
||||
{}
|
||||
|
||||
wkb_t get_wkb_node(osmium::Location const &loc) const;
|
||||
wkbs_t get_wkb_line(osmium::WayNodeList const &nodes, double split_at);
|
||||
wkb_t get_wkb_polygon(osmium::Way const &way);
|
||||
|
||||
wkbs_t get_wkb_multipolygon(osmium::Relation const &rel,
|
||||
osmium::memory::Buffer const &ways,
|
||||
bool build_multigeoms, bool wrap_multi = false);
|
||||
|
||||
wkbs_t get_wkb_multiline(osmium::memory::Buffer const &ways,
|
||||
double split_at);
|
||||
|
||||
/**
|
||||
* Wrap the geometries (must be one or more polygons) in the parameter
|
||||
* into a single multipolygon which is returned in-place in geometries.
|
||||
*/
|
||||
void wrap_in_multipolygon(wkbs_t *geometries);
|
||||
|
||||
/**
|
||||
* Wrap the polygon geometry in the parameter into a multipolygon which
|
||||
* is returned in-place in geometry.
|
||||
*/
|
||||
void wrap_in_multipolygon(wkb_t *geometry);
|
||||
|
||||
private:
|
||||
wkbs_t create_polygons(osmium::Area const &area);
|
||||
size_t add_mp_points(osmium::NodeRefList const &nodes);
|
||||
|
||||
std::shared_ptr<reprojection> m_proj;
|
||||
// internal buffer for creating areas
|
||||
osmium::memory::Buffer m_buffer;
|
||||
ewkb::writer_t m_writer;
|
||||
};
|
||||
|
||||
} // namespace geom
|
||||
|
||||
#endif // OSM2PGSQL_OSMIUM_BUILDER_HPP
|
|
@ -10,6 +10,7 @@
|
|||
#include "db-copy.hpp"
|
||||
#include "expire-tiles.hpp"
|
||||
#include "format.hpp"
|
||||
#include "geom-functions.hpp"
|
||||
#include "geom-transform.hpp"
|
||||
#include "logging.hpp"
|
||||
#include "lua-init.hpp"
|
||||
|
@ -644,12 +645,20 @@ void output_flex_t::write_column(
|
|||
|
||||
void output_flex_t::write_row(table_connection_t *table_connection,
|
||||
osmium::item_type id_type, osmid_t id,
|
||||
std::string const &geom, int srid)
|
||||
geom::geometry_t const &geom, int srid)
|
||||
{
|
||||
assert(table_connection);
|
||||
table_connection->new_line();
|
||||
auto *copy_mgr = table_connection->copy_mgr();
|
||||
|
||||
geom::geometry_t projected_geom;
|
||||
geom::geometry_t const* output_geom = &geom;
|
||||
if (srid && geom.srid() != srid) {
|
||||
auto const proj = reprojection::create_projection(srid);
|
||||
projected_geom = geom::transform(geom, *proj);
|
||||
output_geom = &projected_geom;
|
||||
}
|
||||
|
||||
for (auto const &column : table_connection->table()) {
|
||||
if (column.create_only()) {
|
||||
continue;
|
||||
|
@ -659,19 +668,28 @@ void output_flex_t::write_row(table_connection_t *table_connection,
|
|||
} else if (column.type() == table_column_type::id_num) {
|
||||
copy_mgr->add_column(id);
|
||||
} else if (column.is_geometry_column()) {
|
||||
assert(!geom.empty());
|
||||
copy_mgr->add_hex_geom(geom);
|
||||
assert(!geom.is_null());
|
||||
auto const type = column.type();
|
||||
bool const wrap_multi =
|
||||
(type == table_column_type::multilinestring ||
|
||||
type == table_column_type::multipolygon);
|
||||
copy_mgr->add_hex_geom(geom_to_ewkb(*output_geom, wrap_multi));
|
||||
} else if (column.type() == table_column_type::area) {
|
||||
if (geom.empty()) {
|
||||
if (geom.is_null()) {
|
||||
write_null(copy_mgr, column);
|
||||
} else {
|
||||
// if srid of the area column is the same as for the geom column
|
||||
double const area =
|
||||
column.srid() == srid
|
||||
? ewkb::parser_t(geom)
|
||||
.get_area<osmium::geom::IdentityProjection>()
|
||||
: ewkb::parser_t(geom).get_area<reprojection>(
|
||||
reprojection::create_projection(srid).get());
|
||||
double area = 0;
|
||||
if (column.srid() == 4326) {
|
||||
area = geom::area(geom);
|
||||
} else if (column.srid() == srid) {
|
||||
area = geom::area(projected_geom);
|
||||
} else {
|
||||
// XXX there is some overhead here always dynamically
|
||||
// creating the same projection object. Needs refactoring.
|
||||
auto const mproj = reprojection::create_projection(column.srid());
|
||||
area = geom::area(geom::transform(geom, *mproj));
|
||||
}
|
||||
copy_mgr->add_column(area);
|
||||
}
|
||||
} else {
|
||||
|
@ -1158,32 +1176,27 @@ get_default_transform(flex_table_column_t const &column,
|
|||
column.name())};
|
||||
}
|
||||
|
||||
geom::osmium_builder_t::wkbs_t
|
||||
output_flex_t::run_transform(geom::osmium_builder_t *builder,
|
||||
geom_transform_t const *transform,
|
||||
table_column_type target_geom_type,
|
||||
osmium::Node const &node)
|
||||
geom::geometry_t output_flex_t::run_transform(reprojection const &proj,
|
||||
geom_transform_t const *transform,
|
||||
osmium::Node const &node)
|
||||
{
|
||||
return transform->run(builder, target_geom_type, node);
|
||||
return transform->convert(proj, node);
|
||||
}
|
||||
|
||||
geom::osmium_builder_t::wkbs_t
|
||||
output_flex_t::run_transform(geom::osmium_builder_t *builder,
|
||||
geom_transform_t const *transform,
|
||||
table_column_type target_geom_type,
|
||||
osmium::Way const & /*way*/)
|
||||
geom::geometry_t output_flex_t::run_transform(reprojection const &proj,
|
||||
geom_transform_t const *transform,
|
||||
osmium::Way const & /*way*/)
|
||||
{
|
||||
if (get_way_nodes() <= 1U) {
|
||||
return {};
|
||||
}
|
||||
return transform->run(builder, target_geom_type, m_context_way);
|
||||
|
||||
return transform->convert(proj, *m_context_way);
|
||||
}
|
||||
|
||||
geom::osmium_builder_t::wkbs_t
|
||||
output_flex_t::run_transform(geom::osmium_builder_t *builder,
|
||||
geom_transform_t const *transform,
|
||||
table_column_type target_geom_type,
|
||||
osmium::Relation const &relation)
|
||||
geom::geometry_t output_flex_t::run_transform(reprojection const &proj,
|
||||
geom_transform_t const *transform,
|
||||
osmium::Relation const &relation)
|
||||
{
|
||||
m_buffer.clear();
|
||||
auto const num_ways = middle().rel_members_get(
|
||||
|
@ -1197,7 +1210,7 @@ output_flex_t::run_transform(geom::osmium_builder_t *builder,
|
|||
middle().nodes_get_list(&(way.nodes()));
|
||||
}
|
||||
|
||||
return transform->run(builder, target_geom_type, relation, m_buffer);
|
||||
return transform->convert(proj, relation, m_buffer);
|
||||
}
|
||||
|
||||
template <typename OBJECT>
|
||||
|
@ -1210,7 +1223,7 @@ void output_flex_t::add_row(table_connection_t *table_connection,
|
|||
osmid_t const id = table.map_id(object.type(), object.id());
|
||||
|
||||
if (!table.has_geom_column()) {
|
||||
write_row(table_connection, object.type(), id, "", 0);
|
||||
write_row(table_connection, object.type(), id, {}, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1231,12 +1244,27 @@ void output_flex_t::add_row(table_connection_t *table_connection,
|
|||
transform = get_default_transform(table.geom_column(), object.type());
|
||||
}
|
||||
|
||||
auto *builder = table_connection->get_builder();
|
||||
auto const wkbs =
|
||||
run_transform(builder, transform, table.geom_column().type(), object);
|
||||
for (auto const &wkb : wkbs) {
|
||||
auto const &proj = table_connection->proj();
|
||||
auto const type = table.geom_column().type();
|
||||
|
||||
// The geometry returned by run_transform() is in 4326 if it is a
|
||||
// (multi)polygon. If it is a point or linestring, it is already in the
|
||||
// target geometry.
|
||||
auto const geom = run_transform(proj, transform, object);
|
||||
|
||||
// We need to split a multi geometry into its parts if the geometry
|
||||
// column can only take non-multi geometries or if the transform
|
||||
// explicitly asked us to split, which is the case when an area
|
||||
// transform explicitly set `split_at = 'multi'`.
|
||||
bool const split_multi = type == table_column_type::linestring ||
|
||||
type == table_column_type::polygon ||
|
||||
transform->split();
|
||||
|
||||
auto const geoms = geom::split_multi(geom, split_multi);
|
||||
for (auto const &sgeom : geoms) {
|
||||
auto const wkb = geom_to_ewkb(sgeom);
|
||||
m_expire.from_wkb(wkb, id);
|
||||
write_row(table_connection, object.type(), id, wkb,
|
||||
write_row(table_connection, object.type(), id, sgeom,
|
||||
table.geom_column().srid());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include "expire-tiles.hpp"
|
||||
#include "flex-table-column.hpp"
|
||||
#include "flex-table.hpp"
|
||||
#include "osmium-builder.hpp"
|
||||
#include "geom.hpp"
|
||||
#include "output.hpp"
|
||||
|
||||
#include <osmium/index/id_set.hpp>
|
||||
|
@ -191,21 +191,19 @@ private:
|
|||
flex_table_column_t const &column);
|
||||
void write_row(table_connection_t *table_connection,
|
||||
osmium::item_type id_type, osmid_t id,
|
||||
std::string const &geom, int srid);
|
||||
geom::geometry_t const &geom, int srid);
|
||||
|
||||
geom::osmium_builder_t::wkbs_t
|
||||
run_transform(geom::osmium_builder_t *builder,
|
||||
geom_transform_t const *transform,
|
||||
table_column_type target_geom_type, osmium::Node const &node);
|
||||
geom::geometry_t run_transform(reprojection const &proj,
|
||||
geom_transform_t const *transform,
|
||||
osmium::Node const &node);
|
||||
|
||||
geom::osmium_builder_t::wkbs_t
|
||||
run_transform(geom::osmium_builder_t *builder,
|
||||
geom_transform_t const *transform,
|
||||
table_column_type target_geom_type, osmium::Way const &way);
|
||||
geom::geometry_t run_transform(reprojection const &proj,
|
||||
geom_transform_t const *transform,
|
||||
osmium::Way const &way);
|
||||
|
||||
geom::osmium_builder_t::wkbs_t run_transform(
|
||||
geom::osmium_builder_t *builder, geom_transform_t const *transform,
|
||||
table_column_type target_geom_type, osmium::Relation const &relation);
|
||||
geom::geometry_t run_transform(reprojection const &proj,
|
||||
geom_transform_t const *transform,
|
||||
osmium::Relation const &relation);
|
||||
|
||||
template <typename OBJECT>
|
||||
void add_row(table_connection_t *table_connection, OBJECT const &object);
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
*/
|
||||
|
||||
#include "format.hpp"
|
||||
#include "geom.hpp"
|
||||
#include "geom-from-osm.hpp"
|
||||
#include "geom-functions.hpp"
|
||||
#include "middle.hpp"
|
||||
#include "options.hpp"
|
||||
#include "osmtypes.hpp"
|
||||
|
@ -99,7 +102,8 @@ bool output_gazetteer_t::process_node(osmium::Node const &node)
|
|||
return false;
|
||||
}
|
||||
|
||||
auto const wkb = m_builder.get_wkb_node(node.location());
|
||||
auto const wkb =
|
||||
geom_to_ewkb(geom::transform(geom::create_point(node), *m_proj));
|
||||
delete_unused_classes('N', node.id());
|
||||
m_style.copy_out(node, wkb, m_copy);
|
||||
|
||||
|
@ -132,21 +136,19 @@ bool output_gazetteer_t::process_way(osmium::Way *way)
|
|||
middle().nodes_get_list(&(way->nodes()));
|
||||
|
||||
// Get the geometry of the object.
|
||||
geom::osmium_builder_t::wkb_t geom;
|
||||
geom::geometry_t geom;
|
||||
if (way->is_closed()) {
|
||||
geom = m_builder.get_wkb_polygon(*way);
|
||||
geom = geom::transform(geom::create_polygon(*way), *m_proj);
|
||||
}
|
||||
if (geom.empty()) {
|
||||
auto const wkbs = m_builder.get_wkb_line(way->nodes(), 0.0);
|
||||
if (wkbs.empty()) {
|
||||
if (geom.is_null()) {
|
||||
geom = geom::transform(geom::create_linestring(*way), *m_proj);
|
||||
if (geom.is_null()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
geom = wkbs[0];
|
||||
}
|
||||
|
||||
delete_unused_classes('W', way->id());
|
||||
m_style.copy_out(*way, geom, m_copy);
|
||||
m_style.copy_out(*way, geom_to_ewkb(geom), m_copy);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -200,17 +202,17 @@ bool output_gazetteer_t::process_relation(osmium::Relation const &rel)
|
|||
middle().nodes_get_list(&(w.nodes()));
|
||||
}
|
||||
|
||||
auto const geoms =
|
||||
is_waterway
|
||||
? m_builder.get_wkb_multiline(m_osmium_buffer, 0.0)
|
||||
: m_builder.get_wkb_multipolygon(rel, m_osmium_buffer, true);
|
||||
auto const geom = geom::transform(
|
||||
is_waterway ? geom::create_multilinestring(m_osmium_buffer)
|
||||
: geom::create_multipolygon(rel, m_osmium_buffer),
|
||||
*m_proj);
|
||||
|
||||
if (geoms.empty()) {
|
||||
if (geom.is_null()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
delete_unused_classes('R', rel.id());
|
||||
m_style.copy_out(rel, geoms[0], m_copy);
|
||||
m_style.copy_out(rel, geom_to_ewkb(geom), m_copy);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
#include <osmium/memory/buffer.hpp>
|
||||
|
||||
#include "gazetteer-style.hpp"
|
||||
#include "osmium-builder.hpp"
|
||||
#include "osmtypes.hpp"
|
||||
#include "output.hpp"
|
||||
#include "reprojection.hpp"
|
||||
#include "wkb.hpp"
|
||||
|
||||
class db_copy_thread_t;
|
||||
class thread_pool_t;
|
||||
|
@ -31,7 +32,7 @@ class output_gazetteer_t : public output_t
|
|||
std::shared_ptr<middle_query_t> const &cloned_mid,
|
||||
std::shared_ptr<db_copy_thread_t> const ©_thread)
|
||||
: output_t(cloned_mid, other->m_thread_pool, other->m_options),
|
||||
m_copy(copy_thread), m_builder(other->m_options.projection),
|
||||
m_copy(copy_thread), m_proj(other->m_options.projection),
|
||||
m_osmium_buffer(PLACE_BUFFER_SIZE, osmium::memory::Buffer::auto_grow::yes)
|
||||
{}
|
||||
|
||||
|
@ -41,7 +42,7 @@ public:
|
|||
options_t const &options,
|
||||
std::shared_ptr<db_copy_thread_t> const ©_thread)
|
||||
: output_t(mid, std::move(thread_pool), options), m_copy(copy_thread),
|
||||
m_builder(options.projection),
|
||||
m_proj(options.projection),
|
||||
m_osmium_buffer(PLACE_BUFFER_SIZE, osmium::memory::Buffer::auto_grow::yes)
|
||||
{
|
||||
m_style.load_style(options.style);
|
||||
|
@ -91,7 +92,7 @@ private:
|
|||
gazetteer_copy_mgr_t m_copy;
|
||||
gazetteer_style_t m_style;
|
||||
|
||||
geom::osmium_builder_t m_builder;
|
||||
std::shared_ptr<reprojection> m_proj;
|
||||
osmium::memory::Buffer m_osmium_buffer;
|
||||
};
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "expire-tiles.hpp"
|
||||
#include "geom-from-osm.hpp"
|
||||
#include "geom-functions.hpp"
|
||||
#include "logging.hpp"
|
||||
#include "middle.hpp"
|
||||
#include "options.hpp"
|
||||
|
@ -43,20 +45,33 @@
|
|||
#include "wildcmp.hpp"
|
||||
#include "wkb.hpp"
|
||||
|
||||
static double calculate_area(bool reproject_area,
|
||||
geom::geometry_t const &geom4326,
|
||||
geom::geometry_t const &geom)
|
||||
{
|
||||
if (reproject_area) {
|
||||
// XXX there is some overhead here always dynamically
|
||||
// creating the same projection object. Needs refactoring.
|
||||
auto const ogeom =
|
||||
geom::transform(geom4326, *reprojection::create_projection(3857));
|
||||
return geom::area(ogeom);
|
||||
}
|
||||
return geom::area(geom);
|
||||
}
|
||||
|
||||
void output_pgsql_t::pgsql_out_way(osmium::Way const &way, taglist_t *tags,
|
||||
bool polygon, bool roads)
|
||||
{
|
||||
if (polygon && way.is_closed()) {
|
||||
auto wkb = m_builder.get_wkb_polygon(way);
|
||||
auto const geom = geom::create_polygon(way);
|
||||
auto const projected_geom = geom::transform(geom, *m_proj);
|
||||
|
||||
auto const wkb = geom_to_ewkb(projected_geom);
|
||||
if (!wkb.empty()) {
|
||||
m_expire.from_wkb(wkb, way.id());
|
||||
if (m_enable_way_area) {
|
||||
auto const area =
|
||||
m_options.reproject_area
|
||||
? ewkb::parser_t(wkb).get_area<reprojection>(
|
||||
m_options.projection.get())
|
||||
: ewkb::parser_t(wkb)
|
||||
.get_area<osmium::geom::IdentityProjection>();
|
||||
double const area = calculate_area(m_options.reproject_area,
|
||||
geom, projected_geom);
|
||||
util::double_to_buffer tmp{area};
|
||||
tags->set("way_area", tmp.c_str());
|
||||
}
|
||||
|
@ -65,7 +80,10 @@ void output_pgsql_t::pgsql_out_way(osmium::Way const &way, taglist_t *tags,
|
|||
} else {
|
||||
double const split_at =
|
||||
m_options.projection->target_latlon() ? 1 : 100 * 1000;
|
||||
for (auto const &wkb : m_builder.get_wkb_line(way.nodes(), split_at)) {
|
||||
auto const geoms = geom::split_multi(geom::segmentize(
|
||||
geom::transform(geom::create_linestring(way), *m_proj), split_at));
|
||||
for (auto const &sgeom : geoms) {
|
||||
auto const wkb = geom_to_ewkb(sgeom);
|
||||
m_expire.from_wkb(wkb, way.id());
|
||||
m_tables[t_line]->write_row(way.id(), *tags, wkb);
|
||||
if (roads) {
|
||||
|
@ -147,7 +165,8 @@ void output_pgsql_t::node_add(osmium::Node const &node)
|
|||
return;
|
||||
}
|
||||
|
||||
auto wkb = m_builder.get_wkb_node(node.location());
|
||||
auto const wkb =
|
||||
geom_to_ewkb(geom::transform(geom::create_point(node), *m_proj));
|
||||
m_expire.from_wkb(wkb, node.id());
|
||||
m_tables[t_point]->write_row(node.id(), outtags, wkb);
|
||||
}
|
||||
|
@ -243,8 +262,14 @@ void output_pgsql_t::pgsql_process_relation(osmium::Relation const &rel)
|
|||
if (!make_polygon) {
|
||||
double const split_at =
|
||||
m_options.projection->target_latlon() ? 1 : 100 * 1000;
|
||||
auto wkbs = m_builder.get_wkb_multiline(m_buffer, split_at);
|
||||
for (auto const &wkb : wkbs) {
|
||||
auto geom = geom::line_merge(geom::create_multilinestring(m_buffer));
|
||||
auto projected_geom = geom::transform(geom, *m_proj);
|
||||
if (!projected_geom.is_null() && split_at > 0.0) {
|
||||
projected_geom = geom::segmentize(projected_geom, split_at);
|
||||
}
|
||||
auto const geoms = geom::split_multi(projected_geom);
|
||||
for (auto const &sgeom : geoms) {
|
||||
auto const wkb = geom_to_ewkb(sgeom);
|
||||
m_expire.from_wkb(wkb, -rel.id());
|
||||
m_tables[t_line]->write_row(-rel.id(), outtags, wkb);
|
||||
if (roads) {
|
||||
|
@ -255,18 +280,15 @@ void output_pgsql_t::pgsql_process_relation(osmium::Relation const &rel)
|
|||
|
||||
// multipolygons and boundaries
|
||||
if (make_boundary || make_polygon) {
|
||||
auto wkbs = m_builder.get_wkb_multipolygon(rel, m_buffer,
|
||||
m_options.enable_multi);
|
||||
|
||||
for (auto const &wkb : wkbs) {
|
||||
auto const geoms = geom::split_multi(
|
||||
geom::create_multipolygon(rel, m_buffer), !m_options.enable_multi);
|
||||
for (auto const &sgeom : geoms) {
|
||||
auto const projected_geom = geom::transform(sgeom, *m_proj);
|
||||
auto const wkb = geom_to_ewkb(projected_geom);
|
||||
m_expire.from_wkb(wkb, -rel.id());
|
||||
if (m_enable_way_area) {
|
||||
auto const area =
|
||||
m_options.reproject_area
|
||||
? ewkb::parser_t(wkb).get_area<reprojection>(
|
||||
m_options.projection.get())
|
||||
: ewkb::parser_t(wkb)
|
||||
.get_area<osmium::geom::IdentityProjection>();
|
||||
double const area = calculate_area(m_options.reproject_area,
|
||||
sgeom, projected_geom);
|
||||
util::double_to_buffer tmp{area};
|
||||
outtags.set("way_area", tmp.c_str());
|
||||
}
|
||||
|
@ -391,7 +413,7 @@ output_pgsql_t::output_pgsql_t(
|
|||
std::shared_ptr<middle_query_t> const &mid,
|
||||
std::shared_ptr<thread_pool_t> thread_pool, options_t const &o,
|
||||
std::shared_ptr<db_copy_thread_t> const ©_thread)
|
||||
: output_t(mid, std::move(thread_pool), o), m_builder(o.projection),
|
||||
: output_t(mid, std::move(thread_pool), o), m_proj(o.projection),
|
||||
m_expire(o.expire_tiles_zoom, o.expire_tiles_max_bbox, o.projection),
|
||||
m_buffer(32768, osmium::memory::Buffer::auto_grow::yes),
|
||||
m_rels_buffer(1024, osmium::memory::Buffer::auto_grow::yes)
|
||||
|
@ -449,7 +471,7 @@ output_pgsql_t::output_pgsql_t(
|
|||
std::shared_ptr<db_copy_thread_t> const ©_thread)
|
||||
: output_t(mid, other->m_thread_pool, other->m_options),
|
||||
m_tagtransform(other->m_tagtransform->clone()),
|
||||
m_enable_way_area(other->m_enable_way_area), m_builder(m_options.projection),
|
||||
m_enable_way_area(other->m_enable_way_area), m_proj(m_options.projection),
|
||||
m_expire(m_options.expire_tiles_zoom, m_options.expire_tiles_max_bbox,
|
||||
m_options.projection),
|
||||
m_buffer(1024, osmium::memory::Buffer::auto_grow::yes),
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
#include "db-copy.hpp"
|
||||
#include "expire-tiles.hpp"
|
||||
#include "osmium-builder.hpp"
|
||||
#include "output.hpp"
|
||||
#include "reprojection.hpp"
|
||||
#include "table.hpp"
|
||||
#include "tagtransform.hpp"
|
||||
|
||||
|
@ -89,7 +89,7 @@ protected:
|
|||
|
||||
std::array<std::unique_ptr<table_t>, t_MAX> m_tables;
|
||||
|
||||
geom::osmium_builder_t m_builder;
|
||||
std::shared_ptr<reprojection> m_proj;
|
||||
expire_tiles m_expire;
|
||||
|
||||
osmium::memory::Buffer m_buffer;
|
||||
|
|
|
@ -23,19 +23,22 @@ public:
|
|||
explicit generic_reprojection_t(int srs) : m_target_srs(srs), pj_target(srs)
|
||||
{}
|
||||
|
||||
osmium::geom::Coordinates reproject(osmium::Location loc) const override
|
||||
geom::point_t reproject(geom::point_t point) const override
|
||||
{
|
||||
double const lon = osmium::geom::deg_to_rad(loc.lon_without_check());
|
||||
double const lat = osmium::geom::deg_to_rad(loc.lat_without_check());
|
||||
double const lon = osmium::geom::deg_to_rad(point.x());
|
||||
double const lat = osmium::geom::deg_to_rad(point.y());
|
||||
|
||||
return osmium::geom::transform(pj_source, pj_target,
|
||||
osmium::geom::Coordinates{lon, lat});
|
||||
auto const c = osmium::geom::transform(
|
||||
pj_source, pj_target, osmium::geom::Coordinates{lon, lat});
|
||||
return {c.x, c.y};
|
||||
}
|
||||
|
||||
osmium::geom::Coordinates
|
||||
target_to_tile(osmium::geom::Coordinates coords) const override
|
||||
geom::point_t target_to_tile(geom::point_t point) const override
|
||||
{
|
||||
return osmium::geom::transform(pj_target, pj_tile, coords);
|
||||
auto const c = osmium::geom::transform(
|
||||
pj_target, pj_tile,
|
||||
osmium::geom::Coordinates{point.x(), point.y()});
|
||||
return {c.x, c.y};
|
||||
}
|
||||
|
||||
int target_srs() const noexcept override { return m_target_srs; }
|
||||
|
|
|
@ -20,17 +20,16 @@ public:
|
|||
m_transformation_tile(create_transformation(srs, PROJ_SPHERE_MERC))
|
||||
{}
|
||||
|
||||
osmium::geom::Coordinates reproject(osmium::Location loc) const override
|
||||
geom::point_t
|
||||
reproject(geom::point_t point) const noexcept override
|
||||
{
|
||||
return transform(m_transformation.get(),
|
||||
osmium::geom::Coordinates{loc.lon_without_check(),
|
||||
loc.lat_without_check()});
|
||||
return transform(m_transformation.get(), point);
|
||||
}
|
||||
|
||||
osmium::geom::Coordinates
|
||||
target_to_tile(osmium::geom::Coordinates coords) const override
|
||||
geom::point_t
|
||||
target_to_tile(geom::point_t point) const override
|
||||
{
|
||||
return transform(m_transformation_tile.get(), coords);
|
||||
return transform(m_transformation_tile.get(), point);
|
||||
}
|
||||
|
||||
int target_srs() const noexcept override { return m_target_srs; }
|
||||
|
@ -85,20 +84,19 @@ private:
|
|||
return trans_vis;
|
||||
}
|
||||
|
||||
osmium::geom::Coordinates transform(PJ *transformation,
|
||||
osmium::geom::Coordinates coords) const
|
||||
noexcept
|
||||
geom::point_t transform(PJ *transformation,
|
||||
geom::point_t point) const noexcept
|
||||
{
|
||||
PJ_COORD c_in;
|
||||
c_in.lpzt.z = 0.0;
|
||||
c_in.lpzt.t = HUGE_VAL;
|
||||
c_in.lpzt.lam = coords.x;
|
||||
c_in.lpzt.phi = coords.y;
|
||||
c_in.lpzt.lam = point.x();
|
||||
c_in.lpzt.phi = point.y();
|
||||
|
||||
auto const c_out = proj_trans(transformation, PJ_FWD, c_in);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
|
||||
return osmium::geom::Coordinates{c_out.xy.x, c_out.xy.y};
|
||||
return {c_out.xy.x, c_out.xy.y};
|
||||
}
|
||||
|
||||
int m_target_srs;
|
||||
|
|
|
@ -14,31 +14,31 @@
|
|||
|
||||
namespace {
|
||||
|
||||
osmium::geom::Coordinates lonlat2merc(osmium::geom::Coordinates coords)
|
||||
geom::point_t lonlat2merc(geom::point_t point)
|
||||
{
|
||||
osmium::geom::Coordinates coords{point.x(), point.y()};
|
||||
|
||||
if (coords.y > 89.99) {
|
||||
coords.y = 89.99;
|
||||
} else if (coords.y < -89.99) {
|
||||
coords.y = -89.99;
|
||||
}
|
||||
|
||||
return osmium::geom::lonlat_to_mercator(coords);
|
||||
auto c = osmium::geom::lonlat_to_mercator(coords);
|
||||
return {c.x, c.y};
|
||||
}
|
||||
|
||||
class latlon_reprojection_t : public reprojection
|
||||
{
|
||||
public:
|
||||
osmium::geom::Coordinates reproject(osmium::Location loc) const
|
||||
noexcept override
|
||||
geom::point_t reproject(geom::point_t point) const noexcept override
|
||||
{
|
||||
return osmium::geom::Coordinates{loc.lon_without_check(),
|
||||
loc.lat_without_check()};
|
||||
return point;
|
||||
}
|
||||
|
||||
osmium::geom::Coordinates target_to_tile(osmium::geom::Coordinates c) const
|
||||
noexcept override
|
||||
geom::point_t target_to_tile(geom::point_t point) const noexcept override
|
||||
{
|
||||
return lonlat2merc(c);
|
||||
return lonlat2merc(point);
|
||||
}
|
||||
|
||||
int target_srs() const noexcept override { return PROJ_LATLONG; }
|
||||
|
@ -49,15 +49,12 @@ public:
|
|||
class merc_reprojection_t : public reprojection
|
||||
{
|
||||
public:
|
||||
osmium::geom::Coordinates reproject(osmium::Location loc) const override
|
||||
geom::point_t reproject(geom::point_t coords) const noexcept override
|
||||
{
|
||||
osmium::geom::Coordinates const coords{loc.lon_without_check(),
|
||||
loc.lat_without_check()};
|
||||
return lonlat2merc(coords);
|
||||
}
|
||||
|
||||
osmium::geom::Coordinates target_to_tile(osmium::geom::Coordinates c) const
|
||||
noexcept override
|
||||
geom::point_t target_to_tile(geom::point_t c) const noexcept override
|
||||
{
|
||||
return c;
|
||||
}
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
* It contains the reprojection class.
|
||||
*/
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include "geom.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
@ -44,14 +43,13 @@ public:
|
|||
* Reproject from the source projection lat/lon (EPSG:4326)
|
||||
* to target projection.
|
||||
*/
|
||||
virtual osmium::geom::Coordinates reproject(osmium::Location loc) const = 0;
|
||||
virtual geom::point_t reproject(geom::point_t point) const = 0;
|
||||
|
||||
/**
|
||||
* Converts coordinates from target projection to tile projection
|
||||
* (EPSG:3857)
|
||||
*/
|
||||
virtual osmium::geom::Coordinates
|
||||
target_to_tile(osmium::geom::Coordinates) const = 0;
|
||||
virtual geom::point_t target_to_tile(geom::point_t point) const = 0;
|
||||
|
||||
virtual int target_srs() const noexcept = 0;
|
||||
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
/**
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This file is part of osm2pgsql (https://osm2pgsql.org/).
|
||||
*
|
||||
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
|
||||
* For a full list of authors see the git log.
|
||||
*/
|
||||
|
||||
#include "wkb.hpp"
|
||||
|
||||
namespace ewkb {
|
||||
|
||||
template <typename T>
|
||||
static void str_push(std::string *data, T value)
|
||||
{
|
||||
data->append(reinterpret_cast<char const *const>(&value), sizeof(T));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a EWKB header without length field to the string.
|
||||
*
|
||||
* This header is always 1 + 4 + 4 = 9 bytes long
|
||||
*
|
||||
* \pre \code data != nullptr \endcode
|
||||
*/
|
||||
static void write_header(std::string *data, geometry_type type, uint32_t srid)
|
||||
{
|
||||
str_push(data, Endian);
|
||||
if (srid) {
|
||||
str_push(data, type | geometry_type::wkb_srid);
|
||||
str_push(data, srid);
|
||||
} else {
|
||||
str_push(data, type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a EWKB 32bit unsigned int length field to the string.
|
||||
*
|
||||
* This header is always 4 bytes long
|
||||
*
|
||||
* \pre \code data != nullptr \endcode
|
||||
*/
|
||||
static void write_length(std::string *data, std::size_t length)
|
||||
{
|
||||
str_push(data, static_cast<uint32_t>(length));
|
||||
}
|
||||
|
||||
static void write_points(std::string *data, geom::point_list_t const &points)
|
||||
{
|
||||
write_length(data, points.size());
|
||||
for (auto const &point : points) {
|
||||
str_push(data, point.x());
|
||||
str_push(data, point.y());
|
||||
}
|
||||
}
|
||||
|
||||
static void write_linestring(std::string *data, geom::linestring_t const &geom,
|
||||
uint32_t srid)
|
||||
{
|
||||
assert(data);
|
||||
|
||||
write_header(data, wkb_line, srid);
|
||||
write_points(data, geom);
|
||||
}
|
||||
|
||||
static void write_polygon(std::string *data, geom::polygon_t const &geom,
|
||||
uint32_t srid)
|
||||
{
|
||||
assert(data);
|
||||
|
||||
write_header(data, wkb_polygon, srid);
|
||||
write_length(data, geom.inners().size() + 1);
|
||||
write_points(data, geom.outer());
|
||||
for (auto const &ring : geom.inners()) {
|
||||
write_points(data, ring);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class make_ewkb_visitor
|
||||
{
|
||||
public:
|
||||
make_ewkb_visitor(uint32_t srid, bool ensure_multi) noexcept
|
||||
: m_srid(srid), m_ensure_multi(ensure_multi)
|
||||
{}
|
||||
|
||||
std::string operator()(geom::nullgeom_t const & /*geom*/) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string operator()(geom::point_t const &geom) const
|
||||
{
|
||||
// 9 byte header plus one set of coordinates
|
||||
constexpr const std::size_t size = 9 + 2 * 8;
|
||||
|
||||
std::string data;
|
||||
|
||||
data.reserve(size);
|
||||
write_header(&data, wkb_point, m_srid);
|
||||
str_push(&data, geom.x());
|
||||
str_push(&data, geom.y());
|
||||
|
||||
assert(data.size() == size);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string operator()(geom::linestring_t const &geom) const
|
||||
{
|
||||
std::string data;
|
||||
|
||||
if (m_ensure_multi) {
|
||||
// Two 13 bytes headers plus n sets of coordinates
|
||||
data.reserve(2 * 13 + geom.size() * (2 * 8));
|
||||
write_header(&data, wkb_multi_line, m_srid);
|
||||
write_length(&data, 1);
|
||||
} else {
|
||||
// 13 byte header plus n sets of coordinates
|
||||
data.reserve(13 + geom.size() * (2 * 8));
|
||||
}
|
||||
write_linestring(&data, geom, m_srid);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string operator()(geom::polygon_t const &geom) const
|
||||
{
|
||||
std::string data;
|
||||
|
||||
if (m_ensure_multi) {
|
||||
write_header(&data, wkb_multi_polygon, m_srid);
|
||||
write_length(&data, 1);
|
||||
}
|
||||
write_polygon(&data, geom, m_srid);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string operator()(geom::multipoint_t const & /*geom*/) const
|
||||
{
|
||||
assert(false);
|
||||
return {}; // XXX not used yet, no implementation
|
||||
}
|
||||
|
||||
std::string operator()(geom::multilinestring_t const &geom) const
|
||||
{
|
||||
std::string data;
|
||||
|
||||
write_header(&data, wkb_multi_line, m_srid);
|
||||
write_length(&data, geom.num_geometries());
|
||||
|
||||
for (auto const &line : geom) {
|
||||
write_linestring(&data, line, 0);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string operator()(geom::multipolygon_t const &geom) const
|
||||
{
|
||||
std::string data;
|
||||
|
||||
write_header(&data, wkb_multi_polygon, m_srid);
|
||||
write_length(&data, geom.num_geometries());
|
||||
|
||||
for (auto const &polygon : geom) {
|
||||
write_polygon(&data, polygon, 0);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string operator()(geom::collection_t const & /*geom*/) const
|
||||
{
|
||||
assert(false);
|
||||
return {}; // XXX not used yet, no implementation
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_srid;
|
||||
bool m_ensure_multi;
|
||||
|
||||
}; // class make_ewkb_visitor
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
} // namespace ewkb
|
||||
|
||||
std::string geom_to_ewkb(geom::geometry_t const &geom, bool ensure_multi)
|
||||
{
|
||||
return geom.visit(ewkb::make_ewkb_visitor{
|
||||
static_cast<uint32_t>(geom.srid()), ensure_multi});
|
||||
}
|
301
src/wkb.hpp
301
src/wkb.hpp
|
@ -10,14 +10,22 @@
|
|||
* For a full list of authors see the git log.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include "geom.hpp"
|
||||
|
||||
#include <osmium/geom/coordinates.hpp>
|
||||
#include <osmium/geom/factory.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Functions for converting geometries from and to (E)WKB.
|
||||
*/
|
||||
|
||||
namespace ewkb {
|
||||
|
||||
|
@ -43,199 +51,6 @@ enum wkb_byte_order_type_t : uint8_t
|
|||
#endif
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static void str_push(std::string *str, T data)
|
||||
{
|
||||
assert(str);
|
||||
str->append(reinterpret_cast<char const *const>(&data), sizeof(T));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a EWKB header to the string.
|
||||
*
|
||||
* \pre \code str != nullptr \endcode
|
||||
*/
|
||||
inline std::size_t write_header(std::string *str, geometry_type type,
|
||||
uint32_t srid)
|
||||
{
|
||||
assert(str);
|
||||
|
||||
str_push(str, Endian);
|
||||
str_push(str, type | wkb_srid);
|
||||
str_push(str, srid);
|
||||
|
||||
return str->size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a EWKB header to the string and add a dummy placeholder length of 0.
|
||||
* This can later be replaced by the real length.
|
||||
*
|
||||
* \pre \code str != nullptr \endcode
|
||||
*/
|
||||
inline std::size_t write_header_with_length(std::string *str,
|
||||
geometry_type type, uint32_t srid)
|
||||
{
|
||||
auto const offset = write_header(str, type, srid);
|
||||
str_push(str, static_cast<uint32_t>(0));
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// Create EWKB Point geometry.
|
||||
inline std::string create_point(double x, double y, uint32_t srid = 4326)
|
||||
{
|
||||
std::string data;
|
||||
data.reserve(25); // Point geometries are always 25 bytes
|
||||
|
||||
write_header(&data, wkb_point, srid);
|
||||
str_push(&data, x);
|
||||
str_push(&data, y);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writer for EWKB data suitable for postgres.
|
||||
*
|
||||
* Code has been largely derived from osmium::geom::WKBFactoryImpl.
|
||||
*/
|
||||
class writer_t
|
||||
{
|
||||
public:
|
||||
explicit writer_t(int srid) : m_srid(static_cast<uint32_t>(srid))
|
||||
{
|
||||
assert(srid > 0);
|
||||
}
|
||||
|
||||
void add_sub_geometry(std::string const &part)
|
||||
{
|
||||
assert(!m_data.empty());
|
||||
m_data.append(part);
|
||||
}
|
||||
|
||||
void add_location(osmium::geom::Coordinates const &xy)
|
||||
{
|
||||
assert(!m_data.empty());
|
||||
str_push(&m_data, xy.x);
|
||||
str_push(&m_data, xy.y);
|
||||
}
|
||||
|
||||
/* Point */
|
||||
|
||||
std::string make_point(osmium::geom::Coordinates const &xy) const
|
||||
{
|
||||
return create_point(xy.x, xy.y, m_srid);
|
||||
}
|
||||
|
||||
/* LineString */
|
||||
|
||||
void linestring_start()
|
||||
{
|
||||
assert(m_data.empty());
|
||||
m_geometry_size_offset =
|
||||
write_header_with_length(&m_data, wkb_line, m_srid);
|
||||
}
|
||||
|
||||
std::string linestring_finish(std::size_t num_points)
|
||||
{
|
||||
set_size(m_geometry_size_offset, num_points);
|
||||
std::string data;
|
||||
|
||||
using std::swap;
|
||||
swap(data, m_data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* MultiLineString */
|
||||
|
||||
void multilinestring_start()
|
||||
{
|
||||
assert(m_data.empty());
|
||||
m_multigeometry_size_offset =
|
||||
write_header_with_length(&m_data, wkb_multi_line, m_srid);
|
||||
}
|
||||
|
||||
std::string multilinestring_finish(std::size_t num_lines)
|
||||
{
|
||||
set_size(m_multigeometry_size_offset, num_lines);
|
||||
std::string data;
|
||||
|
||||
using std::swap;
|
||||
swap(data, m_data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Polygon */
|
||||
|
||||
void polygon_start()
|
||||
{
|
||||
assert(m_data.empty());
|
||||
m_geometry_size_offset =
|
||||
write_header_with_length(&m_data, wkb_polygon, m_srid);
|
||||
}
|
||||
|
||||
void polygon_ring_start()
|
||||
{
|
||||
m_ring_size_offset = m_data.size();
|
||||
str_push(&m_data, static_cast<uint32_t>(0));
|
||||
}
|
||||
|
||||
void polygon_ring_finish(std::size_t num_points)
|
||||
{
|
||||
set_size(m_ring_size_offset, num_points);
|
||||
}
|
||||
|
||||
std::string polygon_finish(std::size_t num_rings)
|
||||
{
|
||||
set_size(m_geometry_size_offset, num_rings);
|
||||
std::string data;
|
||||
|
||||
using std::swap;
|
||||
swap(data, m_data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* MultiPolygon */
|
||||
|
||||
void multipolygon_start()
|
||||
{
|
||||
assert(m_data.empty());
|
||||
m_multigeometry_size_offset =
|
||||
write_header_with_length(&m_data, wkb_multi_polygon, m_srid);
|
||||
}
|
||||
|
||||
std::string multipolygon_finish(std::size_t num_polygons)
|
||||
{
|
||||
set_size(m_multigeometry_size_offset, num_polygons);
|
||||
std::string data;
|
||||
|
||||
using std::swap;
|
||||
swap(data, m_data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private:
|
||||
void set_size(std::size_t offset, std::size_t size)
|
||||
{
|
||||
assert(m_data.size() >= offset + sizeof(uint32_t));
|
||||
auto const s = static_cast<uint32_t>(size);
|
||||
std::memcpy(&m_data[offset], reinterpret_cast<char const *>(&s),
|
||||
sizeof(uint32_t));
|
||||
}
|
||||
|
||||
std::string m_data;
|
||||
|
||||
std::size_t m_geometry_size_offset = 0;
|
||||
std::size_t m_multigeometry_size_offset = 0;
|
||||
std::size_t m_ring_size_offset = 0;
|
||||
|
||||
uint32_t m_srid;
|
||||
}; // class writer_t
|
||||
|
||||
/**
|
||||
* Class that allows to iterate over the elements of a ewkb geometry.
|
||||
*
|
||||
|
@ -317,85 +132,7 @@ public:
|
|||
m_pos += length;
|
||||
}
|
||||
|
||||
template <typename PROJ>
|
||||
double get_area(PROJ *proj = nullptr)
|
||||
{
|
||||
double total = 0;
|
||||
|
||||
auto const type = read_header();
|
||||
|
||||
if (type == wkb_polygon) {
|
||||
total = get_polygon_area(proj);
|
||||
} else if (type == wkb_multi_polygon) {
|
||||
auto const num_poly = read_length();
|
||||
for (unsigned i = 0; i < num_poly; ++i) {
|
||||
auto ptype = read_header();
|
||||
(void)ptype;
|
||||
assert(ptype == wkb_polygon);
|
||||
|
||||
total += get_polygon_area(proj);
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename PROJ>
|
||||
double get_polygon_area(PROJ *proj)
|
||||
{
|
||||
auto const num_rings = read_length();
|
||||
assert(num_rings > 0);
|
||||
|
||||
double total = get_ring_area(proj);
|
||||
|
||||
for (unsigned i = 1; i < num_rings; ++i) {
|
||||
total -= get_ring_area(proj);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
template <typename PROJ>
|
||||
double get_ring_area(PROJ *proj)
|
||||
{
|
||||
// Algorithm borrowed from
|
||||
// http://stackoverflow.com/questions/451426/how-do-i-calculate-the-area-of-a-2d-polygon
|
||||
// XXX numerically not stable (useless for latlon)
|
||||
auto const num_pts = read_length();
|
||||
assert(num_pts > 3);
|
||||
|
||||
double total = 0;
|
||||
|
||||
auto prev = proj->target_to_tile(read_point());
|
||||
for (unsigned i = 1; i < num_pts; ++i) {
|
||||
auto const cur = proj->target_to_tile(read_point());
|
||||
total += prev.x * cur.y - cur.x * prev.y;
|
||||
prev = cur;
|
||||
}
|
||||
|
||||
return std::abs(total) * 0.5;
|
||||
}
|
||||
|
||||
double get_ring_area(osmium::geom::IdentityProjection *)
|
||||
{
|
||||
// Algorithm borrowed from
|
||||
// http://stackoverflow.com/questions/451426/how-do-i-calculate-the-area-of-a-2d-polygon
|
||||
auto const num_pts = read_length();
|
||||
assert(num_pts > 3);
|
||||
|
||||
double total = 0;
|
||||
|
||||
auto prev = read_point();
|
||||
for (unsigned i = 1; i < num_pts; ++i) {
|
||||
auto const cur = read_point();
|
||||
total += prev.x * cur.y - cur.x * prev.y;
|
||||
prev = cur;
|
||||
}
|
||||
|
||||
return std::abs(total) * 0.5;
|
||||
}
|
||||
|
||||
void check_available(std::size_t length)
|
||||
{
|
||||
if (m_pos + length > m_wkb->size()) {
|
||||
|
@ -421,4 +158,14 @@ private:
|
|||
|
||||
} // namespace ewkb
|
||||
|
||||
/**
|
||||
* Convert single geometry to EWKB
|
||||
*
|
||||
* \param geom Input geometry
|
||||
* \param ensure_multi Wrap non-multi geometries in multi geometries
|
||||
* \returns String with EWKB encoded geometry
|
||||
*/
|
||||
std::string geom_to_ewkb(geom::geometry_t const &geom,
|
||||
bool ensure_multi = false);
|
||||
|
||||
#endif // OSM2PGSQL_WKB_HPP
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!--
|
||||
Nodes:
|
||||
7 8
|
||||
3 4 11 12
|
||||
9 10
|
||||
1 2 5 6
|
||||
-->
|
||||
<osm version="0.6">
|
||||
<node id="1" version="1" lat="50.0" lon="9.0"/>
|
||||
<node id="2" version="1" lat="50.0" lon="9.1"/>
|
||||
<node id="3" version="1" lat="50.1" lon="9.0"/>
|
||||
<node id="4" version="1" lat="50.1" lon="9.1"/>
|
||||
|
||||
<node id="5" version="1" lat="50.0" lon="9.2"/>
|
||||
<node id="6" version="1" lat="50.0" lon="9.5"/>
|
||||
<node id="7" version="1" lat="50.3" lon="9.2"/>
|
||||
<node id="8" version="1" lat="50.3" lon="9.5"/>
|
||||
|
||||
<node id="9" version="1" lat="50.1" lon="9.3"/>
|
||||
<node id="10" version="1" lat="50.1" lon="9.4"/>
|
||||
<node id="11" version="1" lat="50.2" lon="9.3"/>
|
||||
<node id="12" version="1" lat="50.2" lon="9.4"/>
|
||||
|
||||
<way id="1" version="1">
|
||||
<nd ref="1"/>
|
||||
<nd ref="2"/>
|
||||
<nd ref="4"/>
|
||||
<nd ref="3"/>
|
||||
<nd ref="1"/>
|
||||
<tag k="natural" v="water"/>
|
||||
<tag k="name" v="poly"/>
|
||||
</way>
|
||||
<way id="2" version="1">
|
||||
<nd ref="5"/>
|
||||
<nd ref="6"/>
|
||||
<nd ref="8"/>
|
||||
<nd ref="7"/>
|
||||
<nd ref="5"/>
|
||||
</way>
|
||||
<way id="3" version="1">
|
||||
<nd ref="9"/>
|
||||
<nd ref="10"/>
|
||||
<nd ref="12"/>
|
||||
<nd ref="11"/>
|
||||
<nd ref="9"/>
|
||||
</way>
|
||||
<relation id='1' version='1'>
|
||||
<member type="way" ref="2" role=""/>
|
||||
<member type="way" ref="3" role=""/>
|
||||
<tag k="type" v="multipolygon"/>
|
||||
<tag k="natural" v="water"/>
|
||||
<tag k="name" v="multi"/>
|
||||
</relation>
|
||||
</osm>
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
test = { geom_proj = 25832, area_proj = 25832 }
|
||||
|
||||
dofile(os.getenv('SRCPATH') .. '/data/test_output_flex_area.lua')
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
test = { geom_proj = 25832, area_proj = 3857 }
|
||||
|
||||
dofile(os.getenv('SRCPATH') .. '/data/test_output_flex_area.lua')
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
test = { geom_proj = 25832, area_proj = 4326 }
|
||||
|
||||
dofile(os.getenv('SRCPATH') .. '/data/test_output_flex_area.lua')
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
test = { geom_proj = 3857, area_proj = 25832 }
|
||||
|
||||
dofile(os.getenv('SRCPATH') .. '/data/test_output_flex_area.lua')
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
test = { geom_proj = 3857, area_proj = 4326 }
|
||||
|
||||
dofile(os.getenv('SRCPATH') .. '/data/test_output_flex_area.lua')
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
test = { geom_proj = 4326, area_proj = 25832 }
|
||||
|
||||
dofile(os.getenv('SRCPATH') .. '/data/test_output_flex_area.lua')
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
test = { geom_proj = 4326, area_proj = 3857 }
|
||||
|
||||
dofile(os.getenv('SRCPATH') .. '/data/test_output_flex_area.lua')
|
||||
|
|
@ -11,18 +11,18 @@
|
|||
|
||||
#include "common-buffer.hpp"
|
||||
|
||||
#include "geom-from-osm.hpp"
|
||||
#include "geom-functions.hpp"
|
||||
#include "geom.hpp"
|
||||
#include "reprojection.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
using Coordinates = osmium::geom::Coordinates;
|
||||
|
||||
TEST_CASE("geom::distance", "[NoDB]")
|
||||
{
|
||||
Coordinates const p1{10, 10};
|
||||
Coordinates const p2{20, 10};
|
||||
Coordinates const p3{13, 14};
|
||||
geom::point_t const p1{10, 10};
|
||||
geom::point_t const p2{20, 10};
|
||||
geom::point_t const p3{13, 14};
|
||||
|
||||
REQUIRE(geom::distance(p1, p1) == Approx(0.0));
|
||||
REQUIRE(geom::distance(p1, p2) == Approx(10.0));
|
||||
|
@ -31,20 +31,20 @@ TEST_CASE("geom::distance", "[NoDB]")
|
|||
|
||||
TEST_CASE("geom::interpolate", "[NoDB]")
|
||||
{
|
||||
Coordinates const p1{10, 10};
|
||||
Coordinates const p2{20, 10};
|
||||
geom::point_t const p1{10, 10};
|
||||
geom::point_t const p2{20, 10};
|
||||
|
||||
auto const i1 = geom::interpolate(p1, p1, 0.5);
|
||||
REQUIRE(i1.x == 10);
|
||||
REQUIRE(i1.y == 10);
|
||||
REQUIRE(i1.x() == 10);
|
||||
REQUIRE(i1.y() == 10);
|
||||
|
||||
auto const i2 = geom::interpolate(p1, p2, 0.5);
|
||||
REQUIRE(i2.x == 15);
|
||||
REQUIRE(i2.y == 10);
|
||||
REQUIRE(i2.x() == 15);
|
||||
REQUIRE(i2.y() == 10);
|
||||
|
||||
auto const i3 = geom::interpolate(p2, p1, 0.5);
|
||||
REQUIRE(i3.x == 15);
|
||||
REQUIRE(i3.y == 10);
|
||||
REQUIRE(i3.x() == 15);
|
||||
REQUIRE(i3.y() == 10);
|
||||
}
|
||||
|
||||
TEST_CASE("geom::linestring_t", "[NoDB]")
|
||||
|
@ -52,308 +52,290 @@ TEST_CASE("geom::linestring_t", "[NoDB]")
|
|||
geom::linestring_t ls1;
|
||||
|
||||
REQUIRE(ls1.empty());
|
||||
ls1.add_point(Coordinates{17, 42});
|
||||
ls1.add_point(Coordinates{-3, 22});
|
||||
REQUIRE(!ls1.empty());
|
||||
ls1.emplace_back(17, 42);
|
||||
ls1.emplace_back(-3, 22);
|
||||
REQUIRE(ls1.size() == 2);
|
||||
|
||||
auto it = ls1.cbegin();
|
||||
REQUIRE(it != ls1.cend());
|
||||
REQUIRE(it->x == 17);
|
||||
REQUIRE(it->x() == 17);
|
||||
++it;
|
||||
REQUIRE(it != ls1.cend());
|
||||
REQUIRE(it->y == 22);
|
||||
REQUIRE(it->y() == 22);
|
||||
++it;
|
||||
REQUIRE(it == ls1.cend());
|
||||
}
|
||||
|
||||
TEST_CASE("geom::split_linestring w/o split", "[NoDB]")
|
||||
TEST_CASE("geom::segmentize w/o split", "[NoDB]")
|
||||
{
|
||||
geom::linestring_t const line{Coordinates{0, 0}, Coordinates{1, 2},
|
||||
Coordinates{2, 2}};
|
||||
geom::linestring_t const line{{0, 0}, {1, 2}, {2, 2}};
|
||||
|
||||
std::vector<geom::linestring_t> result;
|
||||
auto const geom = geom::segmentize(geom::geometry_t{line}, 10.0);
|
||||
|
||||
geom::split_linestring(line, 10.0, &result);
|
||||
|
||||
REQUIRE(result.size() == 1);
|
||||
REQUIRE(result[0] == line);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 1);
|
||||
REQUIRE(ml[0] == line);
|
||||
}
|
||||
|
||||
TEST_CASE("geom::split_linestring with split 0.5", "[NoDB]")
|
||||
TEST_CASE("geom::segmentize with split 0.5", "[NoDB]")
|
||||
{
|
||||
geom::linestring_t const line{Coordinates{0, 0}, Coordinates{1, 0}};
|
||||
geom::linestring_t const line{{0, 0}, {1, 0}};
|
||||
|
||||
std::array<geom::linestring_t, 2> const expected{
|
||||
geom::linestring_t{Coordinates{0, 0}, Coordinates{0.5, 0}},
|
||||
geom::linestring_t{Coordinates{0.5, 0}, Coordinates{1, 0}}};
|
||||
geom::linestring_t{{0, 0}, {0.5, 0}},
|
||||
geom::linestring_t{{0.5, 0}, {1, 0}}};
|
||||
|
||||
std::vector<geom::linestring_t> result;
|
||||
auto const geom = geom::segmentize(geom::geometry_t{line}, 0.5);
|
||||
|
||||
geom::split_linestring(line, 0.5, &result);
|
||||
|
||||
REQUIRE(result.size() == 2);
|
||||
REQUIRE(result[0] == expected[0]);
|
||||
REQUIRE(result[1] == expected[1]);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 2);
|
||||
REQUIRE(ml[0] == expected[0]);
|
||||
REQUIRE(ml[1] == expected[1]);
|
||||
}
|
||||
|
||||
TEST_CASE("geom::split_linestring with split 0.4", "[NoDB]")
|
||||
TEST_CASE("geom::segmentize with split 0.4", "[NoDB]")
|
||||
{
|
||||
geom::linestring_t const line{Coordinates{0, 0}, Coordinates{1, 0}};
|
||||
geom::linestring_t const line{{0, 0}, {1, 0}};
|
||||
|
||||
std::array<geom::linestring_t, 3> const expected{
|
||||
geom::linestring_t{Coordinates{0, 0}, Coordinates{0.4, 0}},
|
||||
geom::linestring_t{Coordinates{0.4, 0}, Coordinates{0.8, 0}},
|
||||
geom::linestring_t{Coordinates{0.8, 0}, Coordinates{1, 0}}};
|
||||
geom::linestring_t{{0, 0}, {0.4, 0}},
|
||||
geom::linestring_t{{0.4, 0}, {0.8, 0}},
|
||||
geom::linestring_t{{0.8, 0}, {1, 0}}};
|
||||
|
||||
std::vector<geom::linestring_t> result;
|
||||
auto const geom = geom::segmentize(geom::geometry_t{line}, 0.4);
|
||||
|
||||
geom::split_linestring(line, 0.4, &result);
|
||||
|
||||
REQUIRE(result.size() == 3);
|
||||
REQUIRE(result[0] == expected[0]);
|
||||
REQUIRE(result[1] == expected[1]);
|
||||
REQUIRE(result[2] == expected[2]);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 3);
|
||||
REQUIRE(ml[0] == expected[0]);
|
||||
REQUIRE(ml[1] == expected[1]);
|
||||
REQUIRE(ml[2] == expected[2]);
|
||||
}
|
||||
|
||||
TEST_CASE("geom::split_linestring with split 1.0 at start", "[NoDB]")
|
||||
TEST_CASE("geom::segmentize with split 1.0 at start", "[NoDB]")
|
||||
{
|
||||
geom::linestring_t const line{Coordinates{0, 0}, Coordinates{2, 0},
|
||||
Coordinates{3, 0}, Coordinates{4, 0}};
|
||||
geom::linestring_t const line{{0, 0}, {2, 0}, {3, 0}, {4, 0}};
|
||||
|
||||
std::array<geom::linestring_t, 4> const expected{
|
||||
geom::linestring_t{Coordinates{0, 0}, Coordinates{1, 0}},
|
||||
geom::linestring_t{Coordinates{1, 0}, Coordinates{2, 0}},
|
||||
geom::linestring_t{Coordinates{2, 0}, Coordinates{3, 0}},
|
||||
geom::linestring_t{Coordinates{3, 0}, Coordinates{4, 0}}};
|
||||
geom::linestring_t{{0, 0}, {1, 0}}, geom::linestring_t{{1, 0}, {2, 0}},
|
||||
geom::linestring_t{{2, 0}, {3, 0}}, geom::linestring_t{{3, 0}, {4, 0}}};
|
||||
|
||||
std::vector<geom::linestring_t> result;
|
||||
auto const geom = geom::segmentize(geom::geometry_t{line}, 1.0);
|
||||
|
||||
geom::split_linestring(line, 1.0, &result);
|
||||
|
||||
REQUIRE(result.size() == 4);
|
||||
REQUIRE(result[0] == expected[0]);
|
||||
REQUIRE(result[1] == expected[1]);
|
||||
REQUIRE(result[2] == expected[2]);
|
||||
REQUIRE(result[3] == expected[3]);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 4);
|
||||
REQUIRE(ml[0] == expected[0]);
|
||||
REQUIRE(ml[1] == expected[1]);
|
||||
REQUIRE(ml[2] == expected[2]);
|
||||
REQUIRE(ml[3] == expected[3]);
|
||||
}
|
||||
|
||||
TEST_CASE("geom::split_linestring with split 1.0 in middle", "[NoDB]")
|
||||
TEST_CASE("geom::segmentize with split 1.0 in middle", "[NoDB]")
|
||||
{
|
||||
geom::linestring_t const line{Coordinates{0, 0}, Coordinates{1, 0},
|
||||
Coordinates{3, 0}, Coordinates{4, 0}};
|
||||
geom::linestring_t const line{{0, 0}, {1, 0}, {3, 0}, {4, 0}};
|
||||
|
||||
std::array<geom::linestring_t, 4> const expected{
|
||||
geom::linestring_t{Coordinates{0, 0}, Coordinates{1, 0}},
|
||||
geom::linestring_t{Coordinates{1, 0}, Coordinates{2, 0}},
|
||||
geom::linestring_t{Coordinates{2, 0}, Coordinates{3, 0}},
|
||||
geom::linestring_t{Coordinates{3, 0}, Coordinates{4, 0}}};
|
||||
geom::linestring_t{{0, 0}, {1, 0}}, geom::linestring_t{{1, 0}, {2, 0}},
|
||||
geom::linestring_t{{2, 0}, {3, 0}}, geom::linestring_t{{3, 0}, {4, 0}}};
|
||||
|
||||
std::vector<geom::linestring_t> result;
|
||||
auto const geom = geom::segmentize(geom::geometry_t{line}, 1.0);
|
||||
|
||||
geom::split_linestring(line, 1.0, &result);
|
||||
|
||||
REQUIRE(result.size() == 4);
|
||||
REQUIRE(result[0] == expected[0]);
|
||||
REQUIRE(result[1] == expected[1]);
|
||||
REQUIRE(result[2] == expected[2]);
|
||||
REQUIRE(result[3] == expected[3]);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 4);
|
||||
REQUIRE(ml[0] == expected[0]);
|
||||
REQUIRE(ml[1] == expected[1]);
|
||||
REQUIRE(ml[2] == expected[2]);
|
||||
REQUIRE(ml[3] == expected[3]);
|
||||
}
|
||||
|
||||
TEST_CASE("geom::split_linestring with split 1.0 at end", "[NoDB]")
|
||||
TEST_CASE("geom::segmentize with split 1.0 at end", "[NoDB]")
|
||||
{
|
||||
geom::linestring_t const line{Coordinates{0, 0}, Coordinates{1, 0},
|
||||
Coordinates{2, 0}, Coordinates{4, 0}};
|
||||
geom::linestring_t const line{{0, 0}, {1, 0}, {2, 0}, {4, 0}};
|
||||
|
||||
std::array<geom::linestring_t, 4> const expected{
|
||||
geom::linestring_t{Coordinates{0, 0}, Coordinates{1, 0}},
|
||||
geom::linestring_t{Coordinates{1, 0}, Coordinates{2, 0}},
|
||||
geom::linestring_t{Coordinates{2, 0}, Coordinates{3, 0}},
|
||||
geom::linestring_t{Coordinates{3, 0}, Coordinates{4, 0}}};
|
||||
geom::linestring_t{{0, 0}, {1, 0}}, geom::linestring_t{{1, 0}, {2, 0}},
|
||||
geom::linestring_t{{2, 0}, {3, 0}}, geom::linestring_t{{3, 0}, {4, 0}}};
|
||||
|
||||
std::vector<geom::linestring_t> result;
|
||||
auto const geom = geom::segmentize(geom::geometry_t{line}, 1.0);
|
||||
|
||||
geom::split_linestring(line, 1.0, &result);
|
||||
|
||||
REQUIRE(result.size() == 4);
|
||||
REQUIRE(result[0] == expected[0]);
|
||||
REQUIRE(result[1] == expected[1]);
|
||||
REQUIRE(result[2] == expected[2]);
|
||||
REQUIRE(result[3] == expected[3]);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 4);
|
||||
REQUIRE(ml[0] == expected[0]);
|
||||
REQUIRE(ml[1] == expected[1]);
|
||||
REQUIRE(ml[2] == expected[2]);
|
||||
REQUIRE(ml[3] == expected[3]);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline with single line", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring with single line", "[NoDB]")
|
||||
{
|
||||
geom::linestring_t const expected{Coordinates{1, 1}, Coordinates{2, 1}};
|
||||
geom::linestring_t const expected{{1, 1}, {2, 1}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn10x1y1,n11x2y1");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 1);
|
||||
REQUIRE(lines[0] == expected);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 1);
|
||||
REQUIRE(ml[0] == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline with single line forming a ring", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring with single line forming a ring", "[NoDB]")
|
||||
{
|
||||
geom::linestring_t const expected{Coordinates{1, 1}, Coordinates{2, 1},
|
||||
Coordinates{2, 2}, Coordinates{1, 1}};
|
||||
geom::linestring_t const expected{{1, 1}, {2, 1}, {2, 2}, {1, 1}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn10x1y1,n11x2y1,n12x2y2,n10x1y1");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 1);
|
||||
REQUIRE(lines[0] == expected);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 1);
|
||||
REQUIRE(ml[0] == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline from two non-joined lines", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring from two non-joined lines", "[NoDB]")
|
||||
{
|
||||
std::array<geom::linestring_t, 2> const expected{
|
||||
geom::linestring_t{Coordinates{1, 1}, Coordinates{2, 1}},
|
||||
geom::linestring_t{Coordinates{2, 2}, Coordinates{3, 2}}};
|
||||
geom::linestring_t{{1, 1}, {2, 1}}, geom::linestring_t{{2, 2}, {3, 2}}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn10x1y1,n11x2y1");
|
||||
buffer.add_way("w21 Nn12x2y2,n13x3y2");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 2);
|
||||
REQUIRE(lines[0] == expected[0]);
|
||||
REQUIRE(lines[1] == expected[1]);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 2);
|
||||
REQUIRE(ml[0] == expected[0]);
|
||||
REQUIRE(ml[1] == expected[1]);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline from two lines end to end", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring from two lines end to end", "[NoDB]")
|
||||
{
|
||||
geom::linestring_t const expected{Coordinates{1, 1}, Coordinates{2, 1},
|
||||
Coordinates{2, 2}};
|
||||
geom::linestring_t const expected{{1, 1}, {2, 1}, {2, 2}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn10x1y1,n11x2y1");
|
||||
buffer.add_way("w21 Nn11x2y1,n12x2y2");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 1);
|
||||
REQUIRE(lines[0] == expected);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 1);
|
||||
REQUIRE(ml[0] == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline from two lines with same start point", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring from two lines with same start point",
|
||||
"[NoDB]")
|
||||
{
|
||||
geom::linestring_t const expected{Coordinates{2, 1}, Coordinates{1, 1},
|
||||
Coordinates{1, 2}};
|
||||
geom::linestring_t const expected{{2, 1}, {1, 1}, {1, 2}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn10x1y1,n11x2y1");
|
||||
buffer.add_way("w21 Nn10x1y1,n12x1y2");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 1);
|
||||
REQUIRE(lines[0] == expected);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 1);
|
||||
REQUIRE(ml[0] == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline from two lines with same end point", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring from two lines with same end point", "[NoDB]")
|
||||
{
|
||||
geom::linestring_t const expected{Coordinates{1, 2}, Coordinates{1, 1},
|
||||
Coordinates{2, 1}};
|
||||
geom::linestring_t const expected{{1, 2}, {1, 1}, {2, 1}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn10x1y2,n11x1y1");
|
||||
buffer.add_way("w21 Nn12x2y1,n11x1y1");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 1);
|
||||
REQUIRE(lines[0] == expected);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 1);
|
||||
REQUIRE(ml[0] == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline from two lines connected end to end forming a ring", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring from two lines connected end to end forming "
|
||||
"a ring",
|
||||
"[NoDB]")
|
||||
{
|
||||
geom::linestring_t const expected{Coordinates{1, 1}, Coordinates{2, 1},
|
||||
Coordinates{2, 2}, Coordinates{1, 2},
|
||||
Coordinates{1, 1}};
|
||||
geom::linestring_t const expected{{1, 1}, {2, 1}, {2, 2}, {1, 2}, {1, 1}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn10x1y1,n11x2y1,n13x2y2");
|
||||
buffer.add_way("w21 Nn13x2y2,n12x1y2,n10x1y1");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 1);
|
||||
REQUIRE(lines[0] == expected);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 1);
|
||||
REQUIRE(ml[0] == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline from two lines with same start and end point", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring from two lines with same start and end point",
|
||||
"[NoDB]")
|
||||
{
|
||||
geom::linestring_t const expected{Coordinates{2, 2}, Coordinates{2, 1},
|
||||
Coordinates{1, 1}, Coordinates{1, 2},
|
||||
Coordinates{2, 2}};
|
||||
geom::linestring_t const expected{{2, 2}, {2, 1}, {1, 1}, {1, 2}, {2, 2}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn10x1y1,n11x2y1,n13x2y2");
|
||||
buffer.add_way("w21 Nn10x1y1,n12x1y2,n13x2y2");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 1);
|
||||
REQUIRE(lines[0] == expected);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 1);
|
||||
REQUIRE(ml[0] == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline from three lines, two with same start and end point", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring from three lines, two with same start and "
|
||||
"end point",
|
||||
"[NoDB]")
|
||||
{
|
||||
geom::linestring_t const expected{Coordinates{2, 2}, Coordinates{2, 1},
|
||||
Coordinates{1, 1}, Coordinates{1, 2},
|
||||
Coordinates{2, 2}};
|
||||
geom::linestring_t const expected{{2, 2}, {2, 1}, {1, 1}, {1, 2}, {2, 2}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn10x1y1,n11x2y1,n13x2y2");
|
||||
buffer.add_way("w21 Nn10x1y1,n12x1y2");
|
||||
buffer.add_way("w22 Nn12x1y2,n13x2y2");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 1);
|
||||
REQUIRE(lines[0] == expected);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 1);
|
||||
REQUIRE(ml[0] == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline from four lines forming two rings", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring from four lines forming two rings", "[NoDB]")
|
||||
{
|
||||
std::array<geom::linestring_t, 2> const expected{
|
||||
geom::linestring_t{Coordinates{2, 1}, Coordinates{1, 1},
|
||||
Coordinates{1, 2}},
|
||||
geom::linestring_t{Coordinates{3, 4}, Coordinates{3, 3},
|
||||
Coordinates{4, 3}}};
|
||||
geom::linestring_t{{2, 1}, {1, 1}, {1, 2}},
|
||||
geom::linestring_t{{3, 4}, {3, 3}, {4, 3}}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn10x1y1,n11x2y1");
|
||||
|
@ -361,82 +343,76 @@ TEST_CASE("make_multiline from four lines forming two rings", "[NoDB]")
|
|||
buffer.add_way("w22 Nn13x3y4,n14x3y3");
|
||||
buffer.add_way("w23 Nn15x4y3,n14x3y3");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 2);
|
||||
REQUIRE(lines[0] == expected[0]);
|
||||
REQUIRE(lines[1] == expected[1]);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 2);
|
||||
REQUIRE(ml[0] == expected[0]);
|
||||
REQUIRE(ml[1] == expected[1]);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline from Y shape", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring from Y shape", "[NoDB]")
|
||||
{
|
||||
std::array<geom::linestring_t, 2> const expected{
|
||||
geom::linestring_t{Coordinates{2, 1}, Coordinates{1, 1},
|
||||
Coordinates{1, 2}},
|
||||
geom::linestring_t{
|
||||
Coordinates{1, 1},
|
||||
Coordinates{2, 2},
|
||||
}};
|
||||
geom::linestring_t{{2, 1}, {1, 1}, {1, 2}}, geom::linestring_t{
|
||||
{1, 1},
|
||||
{2, 2},
|
||||
}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn10x1y1,n11x2y1");
|
||||
buffer.add_way("w21 Nn10x1y1,n12x1y2");
|
||||
buffer.add_way("w22 Nn10x1y1,n13x2y2");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 2);
|
||||
REQUIRE(lines[0] == expected[0]);
|
||||
REQUIRE(lines[1] == expected[1]);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 2);
|
||||
REQUIRE(ml[0] == expected[0]);
|
||||
REQUIRE(ml[1] == expected[1]);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline from P shape", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring from P shape", "[NoDB]")
|
||||
{
|
||||
geom::linestring_t const expected{Coordinates{1, 1}, Coordinates{1, 2},
|
||||
Coordinates{1, 3}, Coordinates{2, 3},
|
||||
Coordinates{1, 2}};
|
||||
geom::linestring_t const expected{{1, 1}, {1, 2}, {1, 3}, {2, 3}, {1, 2}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn10x1y1,n11x1y2,n12x1y3");
|
||||
buffer.add_way("w21 Nn12x1y3,n13x2y3,n11x1y2");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 1);
|
||||
REQUIRE(lines[0] == expected);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 1);
|
||||
REQUIRE(ml[0] == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("make_multiline from P shape with closed way", "[NoDB]")
|
||||
TEST_CASE("create_multilinestring from P shape with closed way", "[NoDB]")
|
||||
{
|
||||
std::array<geom::linestring_t, 2> const expected{
|
||||
geom::linestring_t{Coordinates{1, 2}, Coordinates{1, 1}},
|
||||
geom::linestring_t{
|
||||
Coordinates{1, 2},
|
||||
Coordinates{1, 3},
|
||||
Coordinates{2, 3},
|
||||
Coordinates{1, 2},
|
||||
}};
|
||||
geom::linestring_t{{1, 2}, {1, 1}}, geom::linestring_t{
|
||||
{1, 2},
|
||||
{1, 3},
|
||||
{2, 3},
|
||||
{1, 2},
|
||||
}};
|
||||
|
||||
test_buffer_t buffer;
|
||||
buffer.add_way("w20 Nn11x1y2,n12x1y3,n13x2y3,n11x1y2");
|
||||
buffer.add_way("w21 Nn11x1y2,n10x1y1");
|
||||
|
||||
std::vector<geom::linestring_t> lines;
|
||||
auto const geom =
|
||||
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
|
||||
|
||||
auto const proj = reprojection::create_projection(4326);
|
||||
geom::make_multiline(buffer.buffer(), 0.0, *proj, &lines);
|
||||
|
||||
REQUIRE(lines.size() == 2);
|
||||
REQUIRE(lines[0] == expected[0]);
|
||||
REQUIRE(lines[1] == expected[1]);
|
||||
REQUIRE(geom.is_multilinestring());
|
||||
auto const &ml = geom.get<geom::multilinestring_t>();
|
||||
REQUIRE(ml.num_geometries() == 2);
|
||||
REQUIRE(ml[0] == expected[0]);
|
||||
REQUIRE(ml[1] == expected[1]);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,80 +12,111 @@
|
|||
#include "common-import.hpp"
|
||||
#include "common-options.hpp"
|
||||
|
||||
#include "config.h"
|
||||
#include "format.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
static testing::db::import_t db;
|
||||
|
||||
static char const *const conf_file_3857 = "test_output_flex_area_3857.lua";
|
||||
static char const *const conf_file_4326 = "test_output_flex_area_4326.lua";
|
||||
static char const *const conf_file_mix = "test_output_flex_area_mix.lua";
|
||||
static char const *const data_file = "test_output_pgsql_area.osm";
|
||||
static char const *const data_file = "test_output_flex_area.osm";
|
||||
|
||||
TEST_CASE("area calculation in default projection")
|
||||
// Area values for 4326, 3857, 25832 (ETRS89 / UTM zone 32N).
|
||||
// First is for polygon, second for multipolygon.
|
||||
static const double area[3][2] = {{0.01, 0.08},
|
||||
{192987010.0, 1547130000.0},
|
||||
{79600737.5375453234, 635499542.9545904398}};
|
||||
|
||||
static std::array<char const *, 3> const names = {"4326", "3857", "25832"};
|
||||
|
||||
// Indexes into area arrays above
|
||||
enum projs
|
||||
{
|
||||
options_t const options = testing::opt_t().flex(conf_file_3857);
|
||||
p4326 = 0,
|
||||
p3857 = 1,
|
||||
p25832 = 2
|
||||
};
|
||||
|
||||
void check(int p1, int p2)
|
||||
{
|
||||
std::string conf_file{
|
||||
"test_output_flex_area_{}_{}.lua"_format(names[p1], names[p2])};
|
||||
|
||||
options_t const options = testing::opt_t().flex(conf_file.c_str());
|
||||
REQUIRE_NOTHROW(db.run_file(options, data_file));
|
||||
|
||||
auto conn = db.db().connect();
|
||||
|
||||
REQUIRE(2 == conn.get_count("osm2pgsql_test_polygon"));
|
||||
|
||||
// polygon
|
||||
conn.assert_double(
|
||||
1.23927e+10,
|
||||
area[p1][0],
|
||||
"SELECT ST_Area(geom) FROM osm2pgsql_test_polygon WHERE name='poly'");
|
||||
conn.assert_double(
|
||||
area[p2][0],
|
||||
"SELECT area FROM osm2pgsql_test_polygon WHERE name='poly'");
|
||||
conn.assert_double(area[p4326][0],
|
||||
"SELECT ST_Area(ST_Transform(geom, 4326)) "
|
||||
"FROM osm2pgsql_test_polygon WHERE name='poly'");
|
||||
|
||||
// multipolygon
|
||||
conn.assert_double(
|
||||
1.23927e+10,
|
||||
"SELECT ST_Area(geom) FROM osm2pgsql_test_polygon WHERE name='poly'");
|
||||
conn.assert_double(1.0, "SELECT ST_Area(ST_Transform(geom, 4326)) "
|
||||
"FROM osm2pgsql_test_polygon WHERE name='poly'");
|
||||
area[p1][1],
|
||||
"SELECT ST_Area(geom) FROM osm2pgsql_test_polygon WHERE name='multi'");
|
||||
conn.assert_double(
|
||||
9.91828e+10,
|
||||
area[p2][1],
|
||||
"SELECT area FROM osm2pgsql_test_polygon WHERE name='multi'");
|
||||
conn.assert_double(
|
||||
9.91828e+10,
|
||||
"SELECT ST_Area(geom) FROM osm2pgsql_test_polygon WHERE name='multi'");
|
||||
conn.assert_double(8.0, "SELECT ST_Area(ST_Transform(geom, 4326)) "
|
||||
"FROM osm2pgsql_test_polygon WHERE name='multi'");
|
||||
conn.assert_double(area[p4326][1],
|
||||
"SELECT ST_Area(ST_Transform(geom, 4326)) "
|
||||
"FROM osm2pgsql_test_polygon WHERE name='multi'");
|
||||
}
|
||||
|
||||
TEST_CASE("area calculation in latlon projection")
|
||||
TEST_CASE("area and area calculation in latlon (4326) projection")
|
||||
{
|
||||
options_t const options = testing::opt_t().flex(conf_file_4326);
|
||||
|
||||
REQUIRE_NOTHROW(db.run_file(options, data_file));
|
||||
|
||||
auto conn = db.db().connect();
|
||||
|
||||
REQUIRE(2 == conn.get_count("osm2pgsql_test_polygon"));
|
||||
conn.assert_double(
|
||||
1.0, "SELECT area FROM osm2pgsql_test_polygon WHERE name='poly'");
|
||||
conn.assert_double(
|
||||
1.0,
|
||||
"SELECT ST_Area(geom) FROM osm2pgsql_test_polygon WHERE name='poly'");
|
||||
conn.assert_double(
|
||||
8.0, "SELECT area FROM osm2pgsql_test_polygon WHERE name='multi'");
|
||||
conn.assert_double(
|
||||
8.0,
|
||||
"SELECT ST_Area(geom) FROM osm2pgsql_test_polygon WHERE name='multi'");
|
||||
check(p4326, p4326);
|
||||
}
|
||||
|
||||
TEST_CASE("area calculation in latlon projection with way area reprojection")
|
||||
TEST_CASE("area in mercator with area calculation in latlon")
|
||||
{
|
||||
options_t options = testing::opt_t().flex(conf_file_mix);
|
||||
|
||||
REQUIRE_NOTHROW(db.run_file(options, data_file));
|
||||
|
||||
auto conn = db.db().connect();
|
||||
|
||||
REQUIRE(2 == conn.get_count("osm2pgsql_test_polygon"));
|
||||
conn.assert_double(
|
||||
1.23927e+10,
|
||||
"SELECT area FROM osm2pgsql_test_polygon WHERE name='poly'");
|
||||
conn.assert_double(
|
||||
1.0,
|
||||
"SELECT ST_Area(geom) FROM osm2pgsql_test_polygon WHERE name='poly'");
|
||||
conn.assert_double(
|
||||
9.91828e+10,
|
||||
"SELECT area FROM osm2pgsql_test_polygon WHERE name='multi'");
|
||||
conn.assert_double(
|
||||
8.0,
|
||||
"SELECT ST_Area(geom) FROM osm2pgsql_test_polygon WHERE name='multi'");
|
||||
check(p4326, p3857);
|
||||
}
|
||||
|
||||
#ifdef HAVE_GENERIC_PROJ
|
||||
TEST_CASE("area in latlon with area calculation in 25832 projection")
|
||||
{
|
||||
check(p4326, p25832);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("area in latlon with area calculation in mercator projection")
|
||||
{
|
||||
check(p3857, p4326);
|
||||
}
|
||||
|
||||
TEST_CASE("area and area calculation in default (3857) projection")
|
||||
{
|
||||
check(p3857, p3857);
|
||||
}
|
||||
|
||||
#ifdef HAVE_GENERIC_PROJ
|
||||
TEST_CASE("area in mercator with area calculation in 25832 projection")
|
||||
{
|
||||
check(p3857, p25832);
|
||||
}
|
||||
|
||||
TEST_CASE("area in 25832 with area calculation in latlon projection")
|
||||
{
|
||||
check(p25832, p4326);
|
||||
}
|
||||
|
||||
TEST_CASE("area in 25832 with area calculation in mercator projection")
|
||||
{
|
||||
check(p25832, p3857);
|
||||
}
|
||||
|
||||
TEST_CASE("area and area calculation in 25832 projection")
|
||||
{
|
||||
check(p25832, p25832);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -21,13 +21,13 @@ TEST_CASE("projection 4326", "[NoDB]")
|
|||
REQUIRE(reprojection->target_srs() == srs);
|
||||
REQUIRE(reprojection->target_latlon());
|
||||
|
||||
auto const c = reprojection->reproject(loc);
|
||||
REQUIRE(c.x == Approx(10.0));
|
||||
REQUIRE(c.y == Approx(53.0));
|
||||
auto const c = reprojection->reproject(geom::point_t{loc});
|
||||
REQUIRE(c.x() == Approx(10.0));
|
||||
REQUIRE(c.y() == Approx(53.0));
|
||||
|
||||
auto const ct = reprojection->target_to_tile(c);
|
||||
REQUIRE(ct.x == Approx(1113194.91));
|
||||
REQUIRE(ct.y == Approx(6982997.92));
|
||||
REQUIRE(ct.x() == Approx(1113194.91));
|
||||
REQUIRE(ct.y() == Approx(6982997.92));
|
||||
}
|
||||
|
||||
TEST_CASE("projection 3857", "[NoDB]")
|
||||
|
@ -39,13 +39,13 @@ TEST_CASE("projection 3857", "[NoDB]")
|
|||
REQUIRE(reprojection->target_srs() == srs);
|
||||
REQUIRE_FALSE(reprojection->target_latlon());
|
||||
|
||||
auto const c = reprojection->reproject(loc);
|
||||
REQUIRE(c.x == Approx(1113194.91));
|
||||
REQUIRE(c.y == Approx(6982997.92));
|
||||
auto const c = reprojection->reproject(geom::point_t{loc});
|
||||
REQUIRE(c.x() == Approx(1113194.91));
|
||||
REQUIRE(c.y() == Approx(6982997.92));
|
||||
|
||||
auto const ct = reprojection->target_to_tile(c);
|
||||
REQUIRE(ct.x == Approx(1113194.91));
|
||||
REQUIRE(ct.y == Approx(6982997.92));
|
||||
REQUIRE(ct.x() == Approx(1113194.91));
|
||||
REQUIRE(ct.y() == Approx(6982997.92));
|
||||
}
|
||||
|
||||
#ifdef HAVE_GENERIC_PROJ
|
||||
|
@ -58,12 +58,12 @@ TEST_CASE("projection 5651", "[NoDB]")
|
|||
REQUIRE(reprojection->target_srs() == srs);
|
||||
REQUIRE_FALSE(reprojection->target_latlon());
|
||||
|
||||
auto const c = reprojection->reproject(loc);
|
||||
REQUIRE(c.x == Approx(31969448.78));
|
||||
REQUIRE(c.y == Approx(5895222.39));
|
||||
auto const c = reprojection->reproject(geom::point_t{loc});
|
||||
REQUIRE(c.x() == Approx(31969448.78));
|
||||
REQUIRE(c.y() == Approx(5895222.39));
|
||||
|
||||
auto const ct = reprojection->target_to_tile(c);
|
||||
REQUIRE(ct.x == Approx(1113194.91));
|
||||
REQUIRE(ct.y == Approx(6982997.92));
|
||||
REQUIRE(ct.x() == Approx(1113194.91));
|
||||
REQUIRE(ct.y() == Approx(6982997.92));
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue