Commit 5508a4f8 authored by Raúl Marín's avatar Raúl Marín

Integrate wagyu to validate MVT polygons

Closes https://github.com/postgis/postgis/pull/356
Closes #4311



git-svn-id: http://svn.osgeo.org/postgis/trunk@17231 b70326c6-7e19-0410-871a-916f4a2858ee
parent 13409d71
Pipeline #46238272 passed with stage
in 30 minutes
......@@ -7,3 +7,5 @@ coverage:
default:
threshold: 0.2%
comment: false
ignore:
- "deps/wagyu/include/**/*" # Ignore wagyu library files
......@@ -182,3 +182,6 @@ postgis/uninstall_sfcgal.sql
# LLVM JIT
*.bc
*.ll
deps/Makefile
deps/wagyu/Makefile
......@@ -10,6 +10,7 @@ env:
- tag=pg11-geos37-gdal24-proj52 mode=coverage
- tag=pg11-geos37-gdal24-proj52 mode=usan_gcc
- tag=pg11-geos37-gdal24-proj52 mode=usan_clang
- tag=pg11-geos37-gdal24-proj52 mode=wagyu
- tag=pg11-geos37-gdal24-proj52 mode=tests
- tag=pg10-geos36-gdal23-proj49 mode=tests
- tag=pg96-geos36-gdal22-proj49 mode=tests
......
......@@ -6,7 +6,7 @@
SUBDIRS = liblwgeom
ifeq (@LIBLWGEOM_ONLY@,no)
SUBDIRS += libpgcommon postgis regress @RASTER@ @TOPOLOGY@ loader utils doc @EXTENSIONS@
SUBDIRS += libpgcommon postgis regress @RASTER@ @TOPOLOGY@ loader utils doc @EXTENSIONS@ @DEPS_SUBDIR@
endif
POSTGIS_MAJOR_VERSION=@POSTGIS_MAJOR_VERSION@
......
......@@ -26,6 +26,7 @@ PostGIS 3.0.0
within, equals (Esteban Zimányi and Arthur Lesuisse from Université
Libre de Bruxelles (ULB), Darafei Praliaskouski)
- #4171, ST_3DLineInterpolatePoint (Julien Cabieces, Vincent Mora)
- #4311, Introduce `--with-wagyu` as an option for MVT polygons (Raúl Marín)
* Enhancements and fixes *
- #4153, ST_Segmentize now splits segments proportionally (Darafei
......
#!/usr/bin/env bash
set -e
WARNINGS="-Werror -Wall -Wextra -Wformat -Werror=format-security"
WARNINGS_DISABLED="-Wno-unused-parameter -Wno-implicit-fallthrough -Wno-cast-function-type"
# Build with coverage
CFLAGS="-g -O0 --coverage -mtune=generic -fno-omit-frame-pointer ${WARNINGS} ${WARNINGS_DISABLED}"
LDFLAGS="--coverage"
/usr/local/pgsql/bin/pg_ctl -c -l /tmp/logfile start
./autogen.sh
# Standard build
./configure --with-wagyu CFLAGS="${CFLAGS}" CXXFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}"
bash ./ci/travis/logbt -- make -j check RUNTESTFLAGS=--verbose
curl -S -f https://codecov.io/bash -o .github/codecov.bash
bash .github/codecov.bash
......@@ -1421,6 +1421,46 @@ else
fi
dnl ===========================================================================
dnl Deps folder
dnl ===========================================================================
DEPS_MAKEFILE_LIST="
deps/Makefile"
dnl ===========================================================================
dnl Wagyu
dnl ===========================================================================
dnl Wagyu will only be necessary if protobuf is present to build MVTs
HAVE_WAGYU=no
if test "x$HAVE_PROTOBUF" = "xyes"; then
AC_ARG_WITH(
[wagyu],
AC_HELP_STRING([--with-wagyu], [Use the wagyu library]),
[HAVE_WAGYU=yes],
[HAVE_WAGYU=no])
if test "x$HAVE_WAGYU" = "xyes"; then
AC_MSG_RESULT([WAGYU: Wagyu usage requested])
DEPS_SUBDIR="deps"
AC_SUBST([DEPS_SUBDIR])
WAGYU_LIB=libwagyu.la
AC_SUBST([WAGYU_LIB])
AC_PROG_CXX
AX_CXX_COMPILE_STDCXX(11, noext, mandatory)
AC_DEFINE([HAVE_WAGYU], [1], [Define to 1 if wagyu is being built])
AC_SUBST([HAVE_WAGYU])
DEPS_MAKEFILE_LIST="$DEPS_MAKEFILE_LIST
deps/wagyu/Makefile"
fi
fi
......@@ -1471,7 +1511,8 @@ AC_OUTPUT([GNUmakefile
doc/Makefile.comments
doc/html/image_src/Makefile
utils/Makefile
$RT_MAKEFILE_LIST])
$RT_MAKEFILE_LIST
$DEPS_MAKEFILE_LIST])
dnl ===========================================================================
dnl Display the configuration status information
......@@ -1482,6 +1523,9 @@ AC_MSG_RESULT([ PostGIS is now configured for ${host}])
AC_MSG_RESULT()
AC_MSG_RESULT([ -------------- Compiler Info ------------- ])
AC_MSG_RESULT([ C compiler: ${CC} ${CFLAGS}])
if test "x$HAVE_WAGYU" = "xyes"; then
AC_MSG_RESULT([ C++ compiler: ${CXX} ${CXXFLAGS}])
fi
AC_MSG_RESULT([ SQL preprocessor: ${SQLPP}])
AC_MSG_RESULT()
AC_MSG_RESULT([ -------------- Additional Info ------------- ])
......@@ -1520,6 +1564,8 @@ AC_MSG_RESULT([ JSON-C support: ${HAVE_JSON}])
AC_MSG_RESULT([ protobuf-c support: ${HAVE_PROTOBUF}])
AC_MSG_RESULT([ PCRE support: ${HAVE_PCRE}])
AC_MSG_RESULT([ Perl: ${PERL}])
AC_MSG_RESULT([ Wagyu: ${HAVE_WAGYU}])
AC_MSG_RESULT()
AC_MSG_RESULT([ --------------- Extensions --------------- ])
if test "x$RASTER" = "xraster"; then
......
#/**********************************************************************
# *
# * PostGIS - Spatial Types for PostgreSQL
# * http://postgis.net
# *
# * PostGIS is free software: you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation, either version 2 of the License, or
# * (at your option) any later version.
# *
# * PostGIS is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
# *
# **********************************************************************
# *
# * Copyright 2019 Raúl Marín
# *
# **********************************************************************/
CC=@CC@
CXX=@CXX@
CFLAGS=-I../liblwgeom @WARNFLAGS@ @CFLAGS@ @PICFLAGS@
CXXFLAGS=-I../liblwgeom @WARNFLAGS@ @CXXFLAGS@ @PICFLAGS@
LDFLAGS = @LDFLAGS@
top_builddir = @top_builddir@
libdir = @libdir@
LIBTOOL = @LIBTOOL@
all: @WAGYU_LIB@
@WAGYU_LIB@:
$(MAKE) -C wagyu $@
install:
uninstall:
check:
clean:
$(MAKE) -C wagyu $@
distclean: clean
$(MAKE) -C wagyu $@
rm -f Makefile
# LICENSE.geometry applies to:
include/
└── mapbox
├── feature.hpp
├── geometry
│   ├── box.hpp
│   ├── empty.hpp
│   ├── envelope.hpp
│   ├── for_each_point.hpp
│   ├── geometry.hpp
│   ├── line_string.hpp
│   ├── multi_line_string.hpp
│   ├── multi_point.hpp
│   ├── multi_polygon.hpp
│   ├── point_arithmetic.hpp
│   ├── point.hpp
│   └── polygon.hpp
├── geometry.hpp
└── geometry_io.hpp
2 directories, 15 files
# LICENSE.wagyu applies to:
include/
└── mapbox
└── geometry
└── wagyu
├── active_bound_list.hpp
├── bound.hpp
├── bubble_sort.hpp
├── build_edges.hpp
├── build_local_minima_list.hpp
├── build_result.hpp
├── config.hpp
├── edge.hpp
├── interrupt.hpp
├── intersect.hpp
├── intersect_util.hpp
├── local_minimum.hpp
├── local_minimum_util.hpp
├── point.hpp
├── process_horizontal.hpp
├── process_maxima.hpp
├── quick_clip.hpp
├── ring.hpp
├── ring_util.hpp
├── scanbeam.hpp
├── snap_rounding.hpp
├── topology_correction.hpp
├── util.hpp
├── vatti.hpp
└── wagyu.hpp
3 directories, 24 files
Copyright (c) 2016, Mapbox
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
\ No newline at end of file
Parts of the code in the Wagyu Library are derived from the version of the
Clipper Library by Angus Johnson listed below.
Author : Angus Johnson
Version : 6.4.0
Date : 2 July 2015
Website : http://www.angusj.com
Copyright for portions of the derived code in the Wagyu library are held
by Angus Johnson, 2010-2015. All other copyright for the Wagyu Library are held by
Mapbox, 2016. This code is published in accordance with, and retains the same license
as the Clipper Library by Angus Johnson.
Copyright (c) 2010-2015, Angus Johnson
Copyright (c) 2016, Mapbox
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
#/**********************************************************************
# *
# * PostGIS - Spatial Types for PostgreSQL
# * http://postgis.net
# *
# * PostGIS is free software: you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation, either version 2 of the License, or
# * (at your option) any later version.
# *
# * PostGIS is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
# *
# **********************************************************************
# *
# * Copyright 2019 Raúl Marín
# *
# **********************************************************************/
CXX = @CXX@
CXXFLAGS =-I../../liblwgeom -Iinclude @CPPFLAGS@ @WARNFLAGS@ @CXXFLAGS@ @PICFLAGS@
LDFLAGS = @LDFLAGS@
top_builddir = @top_builddir@
libdir = @libdir@
LIBTOOL = @LIBTOOL@
WAGYU_OBJS = \
lwgeom_wagyu.o
WAGYU_HEADERS = \
lwgeom_wagyu.h \
include/mapbox/geometry/polygon.hpp \
include/mapbox/geometry/point_arithmetic.hpp \
include/mapbox/geometry/multi_polygon.hpp \
include/mapbox/geometry/box.hpp \
include/mapbox/geometry/for_each_point.hpp \
include/mapbox/geometry/line_string.hpp \
include/mapbox/geometry/wagyu/process_horizontal.hpp \
include/mapbox/geometry/wagyu/wagyu.hpp \
include/mapbox/geometry/wagyu/ring_util.hpp \
include/mapbox/geometry/wagyu/bubble_sort.hpp \
include/mapbox/geometry/wagyu/snap_rounding.hpp \
include/mapbox/geometry/wagyu/bound.hpp \
include/mapbox/geometry/wagyu/edge.hpp \
include/mapbox/geometry/wagyu/build_result.hpp \
include/mapbox/geometry/wagyu/scanbeam.hpp \
include/mapbox/geometry/wagyu/ring.hpp \
include/mapbox/geometry/wagyu/config.hpp \
include/mapbox/geometry/wagyu/quick_clip.hpp \
include/mapbox/geometry/wagyu/local_minimum_util.hpp \
include/mapbox/geometry/wagyu/process_maxima.hpp \
include/mapbox/geometry/wagyu/intersect.hpp \
include/mapbox/geometry/wagyu/point.hpp \
include/mapbox/geometry/wagyu/util.hpp \
include/mapbox/geometry/wagyu/intersect_util.hpp \
include/mapbox/geometry/wagyu/vatti.hpp \
include/mapbox/geometry/wagyu/active_bound_list.hpp \
include/mapbox/geometry/wagyu/build_local_minima_list.hpp \
include/mapbox/geometry/wagyu/build_edges.hpp \
include/mapbox/geometry/wagyu/local_minimum.hpp \
include/mapbox/geometry/wagyu/topology_correction.hpp \
include/mapbox/geometry/wagyu/interrupt.hpp \
include/mapbox/geometry/geometry.hpp \
include/mapbox/geometry/point.hpp \
include/mapbox/geometry/empty.hpp \
include/mapbox/geometry/multi_line_string.hpp \
include/mapbox/geometry/envelope.hpp \
include/mapbox/geometry/multi_point.hpp \
include/mapbox/feature.hpp \
include/mapbox/geometry_io.hpp \
include/mapbox/geometry.hpp
all: @WAGYU_LIB@
@WAGYU_LIB@: $(WAGYU_OBJS)
ar rs @WAGYU_LIB@ $(WAGYU_OBJS)
$(WAGYU_OBJS): %.o: %.cpp ../../liblwgeom/liblwgeom.h $(WAGYU_HEADERS)
$(CXX) $(CXXFLAGS) -c -o $@ $<
../../liblwgeom/liblwgeom.h:
$(MAKE) -C ../../liblwgeom liblwgeom.h
clean:
rm -f @WAGYU_LIB@ $(WAGYU_OBJS)
distclean: clean
rm -f Makefile
install:
uninstall:
check:
.PHONY: clean distclean install uninstall check
# Wagyu
Wagyu is a library that performs polygon clipping using the [Vatti Clipping Algorithm](https://en.wikipedia.org/wiki/Vatti_clipping_algorithm). It is based on the [Clipper Library](http://www.angusj.com/delphi/clipper.php) but it adds an extra phase to correct the topology and guarantee validation accoding to the OGC specification.
Wagyu is a header only library but depends on [Mapbox Geometry](https://github.com/mapbox/geometry.hpp). It requires a C++ compiler that supports at least C++11.
# liblwgeom bindings
To be able to use Wagyu in Postgis, several functions have been added as a bridge between liblwgeom's C code, and the C++ header only library:
- `lwgeom_wagyu_clip_by_box`: Main function to clip and force validation over a polygon.
- `libwagyu_version`: Returns a static string with the version of the wagyu library.
- `lwgeom_wagyu_interruptRequest`: Function to request the interruption of the wagyu library.
It is integrated in the project as an static library inside postgis.so, so it doesn't require an extra dependency at runtime besides `libstdc++` and `libm` which were a requisite.
# Main considerations about the library
- It works only with POLYGONTYPE or MULTIPOLYGONTYPE type geometries. Anything else will be dropped.
- It's currently setup to use `int32_t` values internally for extra speed. It could be changed to use to `int64_t` or to `double` to match liblwgeom but, as it's only used by MVT, this isn't necessary.
- The library is clipping the geometry to the bounding box passed. It also supports `Intersection` of 2 geometries, `Union`, `Difference` or `XOR` of 2 geometries but those functionalities aren't exposed.
- The library outputs the geometry in the correct winding order for **MVT**, which is the opposite of OGC as the vertical order is switched.
# Dependency changelog
- 2019-02-05 - [Wagyu] Library extraction from https://github.com/mapbox/wagyu
- 2019-02-05 - [geometry.hpp] Library extraction from https://github.com/mapbox/geometry.hpp
#pragma once
#include <mapbox/geometry.hpp>
#include <mapbox/variant.hpp>
#include <cstdint>
#include <string>
#include <vector>
#include <unordered_map>
namespace mapbox {
namespace feature {
struct value;
struct null_value_t
{
};
constexpr bool operator==(const null_value_t&, const null_value_t&) { return true; }
constexpr bool operator!=(const null_value_t&, const null_value_t&) { return false; }
constexpr bool operator<(const null_value_t&, const null_value_t&) { return false; }
constexpr null_value_t null_value = null_value_t();
// Multiple numeric types (uint64_t, int64_t, double) are present in order to support
// the widest possible range of JSON numbers, which do not have a maximum range.
// Implementations that produce `value`s should use that order for type preference,
// using uint64_t for positive integers, int64_t for negative integers, and double
// for non-integers and integers outside the range of 64 bits.
using value_base = mapbox::util::variant<null_value_t, bool, uint64_t, int64_t, double, std::string,
mapbox::util::recursive_wrapper<std::vector<value>>,
mapbox::util::recursive_wrapper<std::unordered_map<std::string, value>>>;
struct value : value_base
{
using value_base::value_base;
};
using property_map = std::unordered_map<std::string, value>;
// The same considerations and requirement for numeric types apply as for `value_base`.
using identifier = mapbox::util::variant<null_value_t, uint64_t, int64_t, double, std::string>;
template <class T>
struct feature
{
using coordinate_type = T;
using geometry_type = mapbox::geometry::geometry<T>; // Fully qualified to avoid GCC -fpermissive error.
geometry_type geometry;
property_map properties;
identifier id;
feature()
: geometry(),
properties(),
id() {}
feature(geometry_type const& geom_)
: geometry(geom_),
properties(),
id() {}
feature(geometry_type&& geom_)
: geometry(std::move(geom_)),
properties(),
id() {}
feature(geometry_type const& geom_, property_map const& prop_)
: geometry(geom_), properties(prop_), id() {}
feature(geometry_type&& geom_, property_map&& prop_)
: geometry(std::move(geom_)),
properties(std::move(prop_)),
id() {}
feature(geometry_type const& geom_, property_map const& prop_, identifier const& id_)
: geometry(geom_),
properties(prop_),
id(id_) {}
feature(geometry_type&& geom_, property_map&& prop_, identifier&& id_)
: geometry(std::move(geom_)),
properties(std::move(prop_)),
id(std::move(id_)) {}
};
template <class T>
constexpr bool operator==(feature<T> const& lhs, feature<T> const& rhs)
{
return lhs.id == rhs.id && lhs.geometry == rhs.geometry && lhs.properties == rhs.properties;
}
template <class T>
constexpr bool operator!=(feature<T> const& lhs, feature<T> const& rhs)
{
return !(lhs == rhs);
}
template <class T, template <typename...> class Cont = std::vector>
struct feature_collection : Cont<feature<T>>
{
using coordinate_type = T;
using feature_type = feature<T>;
using container_type = Cont<feature_type>;
using size_type = typename container_type::size_type;
template <class... Args>
feature_collection(Args&&... args) : container_type(std::forward<Args>(args)...)
{
}
feature_collection(std::initializer_list<feature_type> args)
: container_type(std::move(args)) {}
};
} // namespace feature
} // namespace mapbox
#pragma once
#include <mapbox/geometry/point.hpp>
#include <mapbox/geometry/line_string.hpp>
#include <mapbox/geometry/polygon.hpp>
#include <mapbox/geometry/multi_point.hpp>
#include <mapbox/geometry/multi_line_string.hpp>
#include <mapbox/geometry/multi_polygon.hpp>
#include <mapbox/geometry/geometry.hpp>
#include <mapbox/geometry/point_arithmetic.hpp>
#include <mapbox/geometry/for_each_point.hpp>
#include <mapbox/geometry/envelope.hpp>
#pragma once
#include <mapbox/geometry/point.hpp>
namespace mapbox {
namespace geometry {
template <typename T>
struct box
{
using coordinate_type = T;
using point_type = point<coordinate_type>;
constexpr box(point_type const& min_, point_type const& max_)
: min(min_), max(max_)
{
}
point_type min;
point_type max;
};
template <typename T>
constexpr bool operator==(box<T> const& lhs, box<T> const& rhs)
{
return lhs.min == rhs.min && lhs.max == rhs.max;
}
template <typename T>
constexpr bool operator!=(box<T> const& lhs, box<T> const& rhs)
{
return lhs.min != rhs.min || lhs.max != rhs.max;
}
} // namespace geometry
} // namespace mapbox
#pragma once
namespace mapbox {
namespace geometry {
struct empty
{
}; // this Geometry type represents the empty point set, ∅, for the coordinate space (OGC Simple Features).
constexpr bool operator==(empty, empty) { return true; }
constexpr bool operator!=(empty, empty) { return false; }
constexpr bool operator<(empty, empty) { return false; }
constexpr bool operator>(empty, empty) { return false; }
constexpr bool operator<=(empty, empty) { return true; }
constexpr bool operator>=(empty, empty) { return true; }
} // namespace geometry
} // namespace mapbox
#pragma once
#include <mapbox/geometry/box.hpp>
#include <mapbox/geometry/for_each_point.hpp>
#include <limits>
namespace mapbox {
namespace geometry {
template <typename G, typename T = typename G::coordinate_type>
box<T> envelope(G const& geometry)
{
using limits = std::numeric_limits<T>;
T min_t = limits::has_infinity ? -limits::infinity() : limits::min();
T max_t = limits::has_infinity ? limits::infinity() : limits::max();
point<T> min(max_t, max_t);
point<T> max(min_t, min_t);
for_each_point(geometry, [&](point<T> const& point) {
if (min.x > point.x) min.x = point.x;
if (min.y > point.y) min.y = point.y;
if (max.x < point.x) max.x = point.x;
if (max.y < point.y) max.y = point.y;
});
return box<T>(min, max);
}
} // namespace geometry
} // namespace mapbox
#pragma once
#include <mapbox/geometry/geometry.hpp>
namespace mapbox {
namespace geometry {
template <typename F>
void for_each_point(mapbox::geometry::empty const&, F&&)
{
}
template <typename Point, typename F>
auto for_each_point(Point&& point, F&& f)
-> decltype(point.x, point.y, void())
{
f(std::forward<Point>(point));
}
template <typename Container, typename F>
auto for_each_point(Container&& container, F&& f)
-> decltype(container.begin(), container.end(), void());
template <typename... Types, typename F>
void for_each_point(mapbox::util::variant<Types...> const& geom, F&& f)
{
mapbox::util::variant<Types...>::visit(geom, [&](auto const& g) {
for_each_point(g, f);
});
}
template <typename... Types, typename F>
void for_each_point(mapbox::util::variant<Types...>& geom, F&& f)
{
mapbox::util::variant<Types...>::visit(geom, [&](auto& g) {
for_each_point(g, f);
});
}
template <typename Container, typename F>
auto for_each_point(Container&& container, F&& f)
-> decltype(container.begin(), container.end(), void())
{
for (auto& e : container)
{
for_each_point(e, f);
}
}
} // namespace geometry
} // namespace mapbox
#pragma once
#include <mapbox/geometry/empty.hpp>
#include <mapbox/geometry/point.hpp>
#include <mapbox/geometry/line_string.hpp>
#include <mapbox/geometry/polygon.hpp>
#include <mapbox/geometry/multi_point.hpp>
#include <mapbox/geometry/multi_line_string.hpp>
#include <mapbox/geometry/multi_polygon.hpp>
#include <mapbox/variant.hpp>
// stl