Skip to content

Commit

Permalink
WIP: Add code to generalize data using different strategies
Browse files Browse the repository at this point in the history
This commit adds the code to generalize various types of data using
different strategies.

The following strategies work on a tile-by-tile basis and operate on
polygons:

The "vector-union" strategy buffers and unionizes polygons using vector
operations.

The "raster-union" strategy does a similar thing but does it in raster
space which is much faster. First the polygons are rendered into a
raster, an open/close operation is called (which basically does the same
thing as the buffering in vector space) and finally the resulting raster
is vectorized again.

The "builtup" strategy is intended to derive a layer of builtup areas
from landuse=residential/industrial etc. as well as building cover and
dense road networks. This still needs some work...

Also a new "discrete-isolation" strategy which rates places based on
some importance metric. (This is not tile-based.)

The new "rivers" strategy finds important rivers, this is still very
much work in progress.

For the raster support this adds two new library dependency: CImg and
potrace.

The functionality is accessed through a new command line program called
osm2pgsql-gen. It reads the same Lua config file that osm2pgsql reads.
Call it with -h to get some usage information. This program is for
testing only, eventually the functionality should be accessible from
osm2pgsql itself.

See also https://osm2pgsql.org/generalization/ .
  • Loading branch information
joto committed Feb 26, 2023
1 parent 69c6b4e commit 198a384
Show file tree
Hide file tree
Showing 36 changed files with 3,255 additions and 30 deletions.
2 changes: 2 additions & 0 deletions .github/actions/ubuntu-prerequisites/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ runs:
- name: Install software
run: |
sudo apt-get install -yq --no-install-suggests --no-install-recommends \
cimg-dev \
libboost-filesystem-dev \
libboost-system-dev \
libbz2-dev \
libexpat1-dev \
libpotrace-dev \
libpq-dev \
libproj-dev \
pandoc \
Expand Down
3 changes: 1 addition & 2 deletions .github/actions/win-install/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ runs:

steps:
- name: Install packages
run: vcpkg install bzip2:x64-windows expat:x64-windows zlib:x64-windows proj4:x64-windows boost-geometry:x64-windows boost-system:x64-windows boost-filesystem:x64-windows boost-property-tree:x64-windows lua:x64-windows libpq:x64-windows
run: vcpkg install cimg:x64-windows bzip2:x64-windows expat:x64-windows zlib:x64-windows proj4:x64-windows boost-geometry:x64-windows boost-system:x64-windows boost-filesystem:x64-windows boost-property-tree:x64-windows lua:x64-windows libpq:x64-windows
shell: bash

- name: Install psycopg2 and beahve
run: python -m pip install psycopg2 behave
shell: bash
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

- name: Install prerequisites
run: |
brew install lua boost postgis pandoc
brew install lua boost postgis pandoc cimg potrace
pip3 install psycopg2 behave
pg_ctl -D /usr/local/var/postgres init
pg_ctl -D /usr/local/var/postgres start
Expand Down Expand Up @@ -45,6 +45,7 @@ jobs:
env:
CC: gcc-10
CXX: g++-10
EXTRA_FLAGS: -Wno-unused-but-set-parameter # workaround for GCC bug
LUA_VERSION: 5.3
LUAJIT_OPTION: ON
POSTGRESQL_VERSION: 9.6
Expand Down Expand Up @@ -79,6 +80,7 @@ jobs:
env:
CC: gcc-10
CXX: g++-10
EXTRA_FLAGS: -Wno-unused-but-set-parameter # workaround for GCC bug
LUA_VERSION: 5.3
LUAJIT_OPTION: OFF
POSTGRESQL_VERSION: 10
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/test-install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ jobs:
POSTGRESQL_VERSION: 12
POSTGIS_VERSION: 3
BUILD_TYPE: Release
CXXFLAGS: -pedantic -Wextra -Werror
# -Wno-unused-but-set-parameter is a workaround for GCC bug
CXXFLAGS: -pedantic -Wextra -Werror -Wno-unused-but-set-parameter
PREFIX: /usr/local
OSMURL: https://download.geofabrik.de/europe/monaco-latest.osm.pbf
OSMFILE: monaco-latest.osm.pbf
Expand All @@ -37,12 +38,14 @@ jobs:
run: |
sudo apt-get purge -yq postgresql*
sudo apt-get install -yq --no-install-suggests --no-install-recommends \
cimg-dev \
libboost-filesystem-dev \
libboost-system-dev \
libbz2-dev \
libexpat1-dev \
liblua${LUA_VERSION}-dev \
libluajit-5.1-dev \
libpotrace-dev \
libpq-dev \
libproj-dev \
lua${LUA_VERSION} \
Expand Down
33 changes: 32 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ include_directories(SYSTEM ${PostgreSQL_INCLUDE_DIRS})

find_package(Threads)

find_path(POTRACE_INCLUDE_DIR potracelib.h)
find_library(POTRACE_LIBRARY NAMES potrace)

find_path(CIMG_INCLUDE_DIR CImg.h)

############### Libraries are found now ########################

set(LIBS ${Boost_LIBRARIES} ${PostgreSQL_LIBRARY} ${OSMIUM_LIBRARIES})
Expand Down Expand Up @@ -276,6 +281,32 @@ add_subdirectory(src)
add_executable(osm2pgsql src/osm2pgsql.cpp)
target_link_libraries(osm2pgsql osm2pgsql_lib ${LIBS})

if (${POTRACE_LIBRARY} STREQUAL "POTRACE_LIBRARY-NOTFOUND" OR ${CIMG_INCLUDE_DIR} STREQUAL "CIMG_INCLUDE_DIR-NOTFOUND")
message(STATUS "Did not find cimg and/or potrace library. Not building osm2pgsql-gen.")
else()
if (WITH_LUA)
message(STATUS "Found cimg and potrace library. Building osm2pgsql-gen.")
include_directories(SYSTEM ${CIMG_INCLUDE_DIR})
include_directories(SYSTEM ${POTRACE_INCLUDE_DIR})
add_executable(osm2pgsql-gen src/gen/osm2pgsql-gen.cpp
src/gen/canvas.cpp
src/gen/gen-base.cpp
src/gen/gen-create.cpp
src/gen/gen-discrete-isolation.cpp
src/gen/gen-rivers.cpp
src/gen/gen-tile-builtup.cpp
src/gen/gen-tile-raster.cpp
src/gen/gen-tile-vector.cpp
src/gen/gen-tile.cpp
src/gen/params.cpp
src/gen/raster.cpp
src/gen/tracer.cpp)
target_link_libraries(osm2pgsql-gen osm2pgsql_lib ${LIBS} ${POTRACE_LIBRARY})
else()
message(STATUS "No Lua. Not building osm2pgsql-gen.")
endif()
endif()

#############################################################
# Optional "clang-tidy" target
#############################################################
Expand All @@ -287,7 +318,7 @@ find_program(CLANG_TIDY
if (CLANG_TIDY)
message(STATUS "Looking for clang-tidy - found ${CLANG_TIDY}")

file(GLOB CT_CHECK_FILES src/*.cpp tests/*cpp)
file(GLOB CT_CHECK_FILES src/*.cpp src/*/*.cpp tests/*cpp)

add_custom_target(clang-tidy
${CLANG_TIDY}
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ Required libraries are
* [zlib](https://www.zlib.net/)
* [Boost libraries](https://www.boost.org/), including geometry, system and
filesystem
* [CImg](https://cimg.eu/) (Optional, see README-gen.md)
* [potrace](https://potrace.sourceforge.net/) (Optional, see README-gen.md)
* [PostgreSQL](https://www.postgresql.org/) client libraries
* [Lua](https://www.lua.org/) (Optional, used for Lua tag transforms
and the flex output)
Expand Down Expand Up @@ -80,14 +82,15 @@ On a Debian or Ubuntu system, this can be done with:

```sh
sudo apt-get install make cmake g++ libboost-dev libboost-system-dev \
libboost-filesystem-dev libexpat1-dev zlib1g-dev \
libboost-filesystem-dev libexpat1-dev zlib1g-dev libpotrace-dev cimg-dev \
libbz2-dev libpq-dev libproj-dev lua5.3 liblua5.3-dev pandoc
```

On a Fedora system, use

```sh
sudo dnf install cmake make gcc-c++ boost-devel expat-devel zlib-devel \
potrace-devel cimg-devel \
bzip2-devel postgresql-devel proj-devel proj-epsg lua-devel pandoc
```

Expand Down
130 changes: 130 additions & 0 deletions src/gen/canvas.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This file is part of osm2pgsql (https://osm2pgsql.org/).
*
* Copyright (C) 2006-2023 by the osm2pgsql developer community.
* For a full list of authors see the git log.
*/

#include "canvas.hpp"
#include "raster.hpp"

cimg_library::CImg<int> canvas_t::create_pointlist(geom::point_list_t const &pl,
tile_t const &tile) const
{
cimg_library::CImg<int> points{static_cast<unsigned int>(pl.size()), 2};

int n = 0;
for (auto const point : pl) {
auto const tp = tile.to_tile_coords(point, m_extent);
points(n, 0) = static_cast<int>(static_cast<double>(m_buffer) + tp.x());
points(n, 1) =
static_cast<int>(static_cast<double>(m_buffer + m_extent) - tp.y());
++n;
}

return points;
}

std::size_t canvas_t::draw_polygon(geom::polygon_t const &polygon,
tile_t const &tile)
{
if (polygon.inners().empty()) {
m_rast.draw_polygon(create_pointlist(polygon.outer(), tile), &White);
return polygon.outer().size();
}

std::size_t num_points = polygon.outer().size();
m_temp.draw_polygon(create_pointlist(polygon.outer(), tile), &White);
for (auto const &inner : polygon.inners()) {
num_points += inner.size();
m_temp.draw_polygon(create_pointlist(inner, tile), &Black);
}
m_rast |= m_temp;

return num_points;
}

std::size_t canvas_t::draw_linestring(geom::linestring_t const &linestring,
tile_t const &tile)
{
m_rast.draw_line(create_pointlist(linestring, tile), &White);
return linestring.size();
}

std::size_t canvas_t::draw(geom::geometry_t const &geometry, tile_t const &tile)
{
if (geometry.is_linestring()) {
auto const &linestring = geometry.get<geom::linestring_t>();
return draw_linestring(linestring, tile);
}

if (geometry.is_polygon()) {
auto const &polygon = geometry.get<geom::polygon_t>();
return draw_polygon(polygon, tile);
}

if (geometry.is_multipolygon()) {
auto const &mp = geometry.get<geom::multipolygon_t>();
std::size_t num_points = 0;
for (auto const &p : mp) {
num_points += draw_polygon(p, tile);
}
return num_points;
}

// XXX other geometry types?

return 0;
}

void canvas_t::save(std::string const &filename) const
{
m_rast.save(filename.c_str());
}

std::string canvas_t::to_wkb(tile_t const &tile, double margin) const
{
std::string wkb;
wkb.reserve(61 + 2 + m_rast.size());

// header
wkb_raster_header header{};
header.nBands = 1;
header.scaleX = tile.extent() / static_cast<double>(m_extent);
header.scaleY = -header.scaleX;
header.ipX = tile.xmin(margin);
header.ipY = tile.ymax(margin);
header.width = m_extent + 2 * m_buffer;
header.height = header.width;
add_raster_header(&wkb, header);

// band
wkb_raster_band band{};
band.bits = 4;
add_raster_band(&wkb, band);

// rasterdata
wkb.append(reinterpret_cast<char const *>(m_rast.data()), m_rast.size());

assert(wkb.size() == 61 + 2 + m_rast.size());

return wkb;
}

void canvas_t::merge(canvas_t const &other) { m_rast |= other.m_rast; }

std::string to_hex(std::string const &in)
{
std::string result;
char const *const lookup_hex = "0123456789ABCDEF";

for (const auto c : in) {
unsigned int const num = static_cast<unsigned char>(c);
result += lookup_hex[(num >> 4U) & 0xfU];
result += lookup_hex[num & 0xfU];
}

return result;
}
88 changes: 88 additions & 0 deletions src/gen/canvas.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#ifndef OSM2PGSQL_CANVAS_HPP
#define OSM2PGSQL_CANVAS_HPP

/**
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This file is part of osm2pgsql (https://osm2pgsql.org/).
*
* Copyright (C) 2006-2023 by the osm2pgsql developer community.
* For a full list of authors see the git log.
*/

#include "geom.hpp"
#include "tile.hpp"

#define cimg_display 0 // NOLINT(cppcoreguidelines-macro-usage)
#include "CImg.h"

#include <cstddef>

/**
* This class wraps the image class from the CImg library.
*/
class canvas_t
{
public:
static void info() { cimg_library::cimg::info(); }

/**
* Create a new image canvas. It will be quadratic and have the width and
* height extent + 2*buffer.
*/
canvas_t(std::size_t extent, std::size_t buffer)
: m_extent(extent),
m_buffer(buffer), m_rast{size(), size(), 1, 1, 0}, m_temp{size(), size(),
1, 1, 0}
{}

unsigned int size() const noexcept
{
return static_cast<unsigned int>(m_extent + 2 * m_buffer);
}

unsigned char const *begin() const noexcept { return m_rast.begin(); }
unsigned char const *end() const noexcept { return m_rast.end(); }

std::size_t draw(geom::geometry_t const &geometry, tile_t const &tile);

unsigned char operator()(int x, int y) const noexcept
{
return m_rast(x, y, 0, 0);
}

void open_close(unsigned int buffer_size)
{
m_rast.dilate(buffer_size).erode(buffer_size * 2).dilate(buffer_size);
}

void save(std::string const &filename) const;

std::string to_wkb(tile_t const &tile, double margin) const;

void merge(canvas_t const &other);

private:
constexpr static unsigned char const Black = 0;
constexpr static unsigned char const White = 255;

using image_type = cimg_library::CImg<unsigned char>;

cimg_library::CImg<int> create_pointlist(geom::point_list_t const &pl,
tile_t const &tile) const;

std::size_t draw_polygon(geom::polygon_t const &polygon,
tile_t const &tile);

std::size_t draw_linestring(geom::linestring_t const &linestring,
tile_t const &tile);

std::size_t m_extent;
std::size_t m_buffer;
image_type m_rast;
image_type m_temp;
}; // class canvas_t

std::string to_hex(std::string const &in);

#endif // OSM2PGSQL_CANVAS_HPP
Loading

0 comments on commit 198a384

Please sign in to comment.