Merge remote-tracking branch 'origin/master' into nested_namespace

This commit is contained in:
Dennis Luxen 2022-12-11 10:17:31 +01:00
commit d7c44f0bc0
43 changed files with 115 additions and 4294 deletions

View File

@ -1,8 +1,8 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.18)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR AND NOT MSVC_IDE)
message(FATAL_ERROR "In-source builds are not allowed.
@ -33,7 +33,6 @@ option(ENABLE_COVERAGE "Build with coverage instrumentalisation" OFF)
option(ENABLE_SANITIZER "Use memory sanitizer for Debug build" OFF)
option(ENABLE_LTO "Use LTO if available" OFF)
option(ENABLE_FUZZING "Fuzz testing using LLVM's libFuzzer" OFF)
option(ENABLE_GOLD_LINKER "Use GNU gold linker if available" ON)
option(ENABLE_NODE_BINDINGS "Build NodeJs bindings" OFF)
option(ENABLE_CLANG_TIDY "Enables clang-tidy checks" OFF)
@ -113,29 +112,12 @@ include(CheckCXXCompilerFlag)
include(FindPackageHandleStandardArgs)
include(GNUInstallDirs)
set(bitness 32)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(bitness 64)
message(STATUS "Building on a 64 bit system")
else()
message(STATUS "Building on a 32 bit system")
endif()
if(WIN32 AND MSVC_VERSION LESS 1900)
message(FATAL_ERROR "Building with Microsoft compiler needs Latest Visual Studio 2015 (Community or better)")
endif()
# Strictly require GCC>=5.0 and Clang>=3.4 - GCC 4.8 is already too old for C++14.
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
message(FATAL_ERROR "GCC>=5.0 required. In case you are on Ubuntu upgrade via ppa:ubuntu-toolchain-r/test")
endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4)
message(FATAL_ERROR "Clang>=3.4 required. In case you are on Ubuntu upgrade via http://apt.llvm.org")
endif()
endif()
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/include/)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include/)
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/sol2-3.3.0/include)
@ -190,26 +172,6 @@ add_library(osrm_customize src/osrm/customizer.cpp $<TARGET_OBJECTS:CUSTOMIZER>
add_library(osrm_update $<TARGET_OBJECTS:UPDATER> $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL>)
add_library(osrm_store $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL>)
if(ENABLE_GOLD_LINKER)
execute_process(COMMAND ${CMAKE_C_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
if("${LD_VERSION}" MATCHES "GNU gold")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold -Wl,--disable-new-dtags")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=gold -Wl,--disable-new-dtags")
set(OSRM_LDFLAGS "${OSRM_LDFLAGS} -fuse-ld=gold -Wl,--disable-new-dtags")
message(STATUS "Using GNU gold as linker.")
# Issue 2785: check gold binutils version and don't use gc-sections for versions prior 2.25
string(REGEX REPLACE ".*\\(GNU Binutils[^\\)0-9]+([0-9]+\\.[0-9]+)[^\\)]*\\).*" "\\1" GOLD_BINUTILS_VERSION "${LD_VERSION}")
if ("${GOLD_BINUTILS_VERSION}" VERSION_LESS "2.26")
message(STATUS "Disabling gc-sections on gold binutils < 2.26, see: https://sourceware.org/bugzilla/show_bug.cgi?id=17639")
set(LD_AVOID_GC_SECTIONS TRUE)
endif()
else()
message(WARNING "GNU gold linker isn't available.")
set(ENABLE_GOLD_LINKER OFF)
endif()
endif()
# Explicitly set the build type to Release if no other type is specified
# on the command line. Without this, cmake defaults to an unoptimized,
# non-debug build, which almost nobody wants.
@ -247,74 +209,17 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -ggdb")
endif()
if(CMAKE_BUILD_TYPE MATCHES Release OR CMAKE_BUILD_TYPE MATCHES MinRelSize OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
message(STATUS "Configuring release mode optimizations")
# Check if LTO is available
check_cxx_compiler_flag("-Wl,-flto" LTO_AVAILABLE)
if(ENABLE_LTO AND LTO_AVAILABLE)
set(OLD_CXX_FLAGS ${CMAKE_CXX_FLAGS})
# GCC in addition allows parallelizing LTO
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
include(ProcessorCount)
ProcessorCount(NPROC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto=${NPROC}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto")
endif()
set(CHECK_LTO_SRC "int main(){return 0;}")
check_cxx_source_compiles("${CHECK_LTO_SRC}" LTO_WORKS)
if(LTO_WORKS)
message(STATUS "LTO working")
set(OSRM_CXXFLAGS "${OSRM_CXXFLAGS} -flto")
set(OSRM_LDFLAGS "${OSRM_LDFLAGS} -flto")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -flto")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -flto")
else()
message(STATUS "LTO broken")
set(CMAKE_CXX_FLAGS "${OLD_CXX_FLAGS}")
set(ENABLE_LTO Off)
endif()
# Since gcc 4.9 the LTO format is non-standart ('slim'), so we need to use the build-in tools
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND NOT MINGW)
find_program(GCC_AR gcc-ar)
find_program(GCC_RANLIB gcc-ranlib)
if ("${GCC_AR}" STREQUAL "GCC_AR-NOTFOUND" OR "${GCC_RANLIB}" STREQUAL "GCC_RANLIB-NOTFOUND")
message(WARNING "GCC specific binutils not found. In case of linker issues export env vars: AR=gcc-ar, NM=gcc-nm, RANLIB=gcc-ranlib")
else()
message(STATUS "Using GCC specific binutils for LTO:")
message(STATUS " ${GCC_AR}")
message(STATUS " ${GCC_RANLIB}")
set(CMAKE_AR ${GCC_AR})
set(CMAKE_RANLIB ${GCC_RANLIB})
endif()
endif()
# Same for clang LTO requires their own toolchain
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
find_program(LLVM_AR llvm-ar)
find_program(LLVM_RANLIB llvm-ranlib)
if ("${LLVM_AR}" STREQUAL "LLVM_AR-NOTFOUND" OR "${LLVM_RANLIB}" STREQUAL "LLVM_RANLIB-NOTFOUND")
message(WARNING "LLVM specific binutils not found.")
else()
message(STATUS "Using LLVM specific binutils for LTO:")
message(STATUS " ${LLVM_AR}")
message(STATUS " ${LLVM_RANLIB}")
set(CMAKE_AR ${LLVM_AR})
set(CMAKE_RANLIB ${LLVM_RANLIB})
endif()
endif()
if(ENABLE_LTO AND (CMAKE_BUILD_TYPE MATCHES Release OR CMAKE_BUILD_TYPE MATCHES MinRelSize OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo))
include(CheckIPOSupported)
check_ipo_supported(RESULT LTO_SUPPORTED OUTPUT error)
if(LTO_SUPPORTED)
message(STATUS "IPO / LTO enabled")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
else()
message(WARNING "IPO / LTO not supported: <${error}>")
endif()
endif()
# TODO: this was added for Mason, we are not sure if it is true after migration to Conan
if(UNIX AND NOT APPLE AND ENABLE_CONAN AND (LTO_WORKS OR ENABLE_GOLD_LINKER))
message(WARNING "ENABLE_CONAN and ENABLE_LTO/ENABLE_GOLD_LINKER may not work on all linux systems currently")
endif()
set(MAYBE_COVERAGE_LIBRARIES "")
if (ENABLE_COVERAGE)
if (NOT CMAKE_BUILD_TYPE MATCHES "Debug")
@ -352,14 +257,6 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
add_dependency_defines(-DWIN32)
set(OPTIONAL_SOCKET_LIBS ws2_32 wsock32)
endif()
# -fpermissive is required for parallel_do Intel TBB internal issue with GCC < 5
# https://github.com/Project-OSRM/osrm-backend/pull/3603#issuecomment-277688589
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
message(STATUS "Adding -fpermissive for GCC version < 5 bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51048). See #3603.")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive")
endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
# using Intel C++
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-intel -wd10237 -Wall -ipo -fPIC")
@ -377,48 +274,6 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
target_link_libraries(osrm-extract wsock32 ws2_32)
endif()
# Configuring linker
execute_process(COMMAND ${CMAKE_CXX_COMPILER} "-Wl,--version" ERROR_QUIET OUTPUT_VARIABLE LINKER_VERSION)
# For ld.gold and ld.bfs (the GNU linkers) we optimize hard
if("${LINKER_VERSION}" MATCHES "GNU gold" OR "${LINKER_VERSION}" MATCHES "GNU ld")
message(STATUS "Setting linker optimizations")
if(NOT (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR "${LD_AVOID_GC_SECTIONS}"))
# Tell compiler to put every function in separate section, linker can then match sections and functions
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections")
# Tell linker to do dead code and data eminination during link time discarding sections
set(LINKER_FLAGS "${LINKER_FLAGS} -Wl,--gc-sections")
endif()
# Default linker optimization flags
set(LINKER_FLAGS "${LINKER_FLAGS} -Wl,-O1 -Wl,--hash-style=gnu -Wl,--sort-common")
else()
message(STATUS "Using unknown linker, not setting linker optimizations")
endif ()
set(OSRM_LDFLAGS "${OSRM_LDFLAGS} ${LINKER_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LINKER_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}")
# Activate C++1y
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
set(OSRM_CXXFLAGS "${OSRM_CXXFLAGS} -std=c++17")
endif()
# Configuring other platform dependencies
if(APPLE)
execute_process(COMMAND xcrun --sdk macosx --show-sdk-path OUTPUT_VARIABLE CMAKE_OSX_SYSROOT OUTPUT_STRIP_TRAILING_WHITESPACE)
exec_program(uname ARGS -v OUTPUT_VARIABLE DARWIN_VERSION)
string(REGEX MATCH "[0-9]+" DARWIN_VERSION ${DARWIN_VERSION})
if(OSXLIBSTD)
message(STATUS "linking against ${OSXLIBSTD}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=${OSXLIBSTD}")
elseif(DARWIN_VERSION GREATER 12)
message(STATUS "linking against libc++")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
endif()
if(UNIX AND NOT APPLE)
set(MAYBE_RT_LIBRARY -lrt)
endif()
@ -432,11 +287,6 @@ include_directories(SYSTEM ${RAPIDJSON_INCLUDE_DIR})
set(MICROTAR_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src")
include_directories(SYSTEM ${MICROTAR_INCLUDE_DIR})
set(MBXGEOM_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/geometry.hpp-0.9.2/include")
include_directories(SYSTEM ${MBXGEOM_INCLUDE_DIR})
set(CHEAPRULER_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cheap-ruler-cpp-2778eb8/include")
include_directories(SYSTEM ${CHEAPRULER_INCLUDE_DIR})
add_library(MICROTAR OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src/microtar.c")
set_property(TARGET MICROTAR PROPERTY POSITION_INDEPENDENT_CODE ON)
target_no_warning(MICROTAR unused-variable)
@ -550,12 +400,7 @@ if(ENABLE_CONAN)
# expat and bzip2 are used from conan rather than the system
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/libosmium/include)
else()
if (BUILD_ROUTED)
# osrm-routed requires newer boost:asio
find_package(Boost 1.70 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
else()
find_package(Boost 1.60 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
endif()
find_package(Boost 1.70 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
add_dependency_includes(${Boost_INCLUDE_DIRS})
find_package(TBB REQUIRED)
@ -602,7 +447,6 @@ if(ENABLE_CCACHE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILE
message(STATUS "Using ccache to speed up incremental builds")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
set(ENV{CCACHE_CPP2} "true")
endif()
endif()

View File

@ -1,29 +0,0 @@
# - Check whether the CXX compiler supports a given flag.
# CHECK_CXX_COMPILER_FLAG(<flag> <var>)
# <flag> - the compiler flag
# <var> - variable to store the result
# This internally calls the check_cxx_source_compiles macro. See help
# for CheckCXXSourceCompiles for a listing of variables that can
# modify the build.
# Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
INCLUDE(CheckCXXSourceCompiles)
MACRO (CHECK_CXX_COMPILER_FLAG _FLAG _RESULT)
SET(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
SET(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
CHECK_CXX_SOURCE_COMPILES("int main() { return 0;}" ${_RESULT}
# Some compilers do not fail with a bad flag
FAIL_REGEX "unrecognized .*option" # GNU
FAIL_REGEX "ignoring unknown option" # MSVC
FAIL_REGEX "[Uu]nknown option" # HP
FAIL_REGEX "[Ww]arning: [Oo]ption" # SunPro
FAIL_REGEX "command option .* is not recognized" # XL
)
SET (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
ENDMACRO (CHECK_CXX_COMPILER_FLAG)

View File

@ -36,7 +36,10 @@
# This is because, the lua location is not standardized and may exist in
# locations other than lua/
include(FindPkgConfig)
if(NOT PKG_CONFIG_FOUND)
include(CMakeFindDependencyMacro)
find_dependency(PkgConfig)
endif()
unset(_lua_include_subdirs)
unset(_lua_library_names)

View File

@ -1,7 +1,7 @@
# https://github.com/sbellus/json-cmake/blob/9913da8800b95322d393894d3525d634568f305e/JSONParser.cmake
# MIT Licensed - https://github.com/sbellus/json-cmake/blob/master/LICENSE
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.18)
if (DEFINED JSonParserGuard)
return()

View File

@ -5,7 +5,7 @@ RUN mkdir -p /src && mkdir -p /opt
RUN apt-get update && \
apt-get -y --no-install-recommends install ca-certificates cmake make git gcc g++ libbz2-dev libxml2-dev wget \
libzip-dev libboost1.74-all-dev lua5.4 liblua5.4-dev -o APT::Install-Suggests=0 -o APT::Install-Recommends=0
libzip-dev libboost1.74-all-dev lua5.4 liblua5.4-dev pkg-config -o APT::Install-Suggests=0 -o APT::Install-Recommends=0
RUN NPROC=${BUILD_CONCURRENCY:-$(nproc)} && \
ldconfig /usr/local/lib && \

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.8)
cmake_minimum_required(VERSION 3.18)
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR AND NOT MSVC_IDE)
message(FATAL_ERROR "In-source builds are not allowed.
@ -12,20 +12,18 @@ endif()
project(osrm-example C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
set(bitness 32)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(bitness 64)
message(STATUS "Building on a 64 bit system")
else()
message(STATUS "Building on a 32 bit system")
endif()
if(WIN32 AND MSVC_VERSION LESS 1900)
message(FATAL_ERROR "Building with Microsoft compiler needs Latest Visual Studio 2015 (Community or better)")
endif()
link_directories(${LibOSRM_LIBRARY_DIRS})
add_executable(osrm-example example.cpp)

View File

@ -0,0 +1,86 @@
#pragma once
#include <cassert>
#include <cmath>
#include <cstdint>
#include <limits>
#include <tuple>
#include <utility>
namespace mapbox
{
namespace geometry
{
template <typename T> struct point
{
using coordinate_type = T;
constexpr point() : x(), y() {}
constexpr point(T x_, T y_) : x(x_), y(y_) {}
T x;
T y;
};
} // namespace geometry
namespace cheap_ruler
{
using point = geometry::point<double>;
class CheapRuler
{
// Values that define WGS84 ellipsoid model of the Earth
static constexpr double RE = 6378.137; // equatorial radius
static constexpr double FE = 1.0 / 298.257223563; // flattening
static constexpr double E2 = FE * (2 - FE);
static constexpr double RAD = M_PI / 180.0;
public:
explicit CheapRuler(double latitude)
{
// Curvature formulas from https://en.wikipedia.org/wiki/Earth_radius#Meridional
double mul = RAD * RE * 1000;
double coslat = std::cos(latitude * RAD);
double w2 = 1 / (1 - E2 * (1 - coslat * coslat));
double w = std::sqrt(w2);
// multipliers for converting longitude and latitude degrees into distance
kx = mul * w * coslat; // based on normal radius of curvature
ky = mul * w * w2 * (1 - E2); // based on meridonal radius of curvature
}
double squareDistance(point a, point b) const
{
auto dx = longDiff(a.x, b.x) * kx;
auto dy = (a.y - b.y) * ky;
return dx * dx + dy * dy;
}
//
// Given two points of the form [x = longitude, y = latitude], returns the distance.
//
double distance(point a, point b) const { return std::sqrt(squareDistance(a, b)); }
//
// Returns the bearing between two points in angles.
//
double bearing(point a, point b) const
{
auto dx = longDiff(b.x, a.x) * kx;
auto dy = (b.y - a.y) * ky;
return std::atan2(dx, dy) / RAD;
}
private:
double ky;
double kx;
static double longDiff(double a, double b) { return std::remainder(a - b, 360); }
};
} // namespace cheap_ruler
} // namespace mapbox

View File

@ -8,13 +8,13 @@ namespace osrm::util
template <typename It, typename Value> inline void static_assert_iter_value()
{
using IterValueType = typename std::iterator_traits;<It>::value_type;
using IterValueType = typename std::iterator_traits<It>::value_type;
static_assert(std::is_same<IterValueType, Value>::value, "");
}
template <typename It, typename Category> inline void static_assert_iter_category()
{
using IterCategoryType = typename std::iterator_traits;<It>::iterator_category;
using IterCategoryType = typename std::iterator_traits<It>::iterator_category;
static_assert(std::is_base_of<Category, IterCategoryType>::value, "");
}

View File

@ -1,6 +1,4 @@
# node-cmake requires CMake 3.1 features; for the osrm project we only
# require CMake 2.8.11 so that we can build e.g. on Trusty by default.
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.18)
message(STATUS "Building node_osrm")

View File

@ -1,12 +1,11 @@
#include "util/coordinate_calculation.hpp"
#include "util/cheap_ruler.hpp"
#include "util/coordinate.hpp"
#include "util/trigonometry_table.hpp"
#include "util/web_mercator.hpp"
#include <boost/assert.hpp>
#include <mapbox/cheap_ruler.hpp>
#include <algorithm>
#include <iterator>
#include <limits>
@ -26,8 +25,8 @@ class CheapRulerContainer
{
for (int n = 0; n < number_of_rulers; n++)
{
cheap_ruler_cache[n] = mapbox::cheap_ruler::CheapRuler(
step * (n + 0.5) / COORDINATE_PRECISION, mapbox::cheap_ruler::CheapRuler::Meters);
cheap_ruler_cache[n] =
mapbox::cheap_ruler::CheapRuler(step * (n + 0.5) / COORDINATE_PRECISION);
}
};

View File

@ -1,18 +0,0 @@
Standard: Cpp11
IndentWidth: 4
AccessModifierOffset: -4
UseTab: Never
BinPackParameters: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
AlwaysBreakTemplateDeclarations: true
NamespaceIndentation: None
PointerBindsToType: true
SpacesInParentheses: false
BreakBeforeBraces: Attach
ColumnLimit: 100
Cpp11BracedListStyle: false
SpacesBeforeTrailingComments: 1

View File

@ -1,2 +0,0 @@
build/
mason_packages/

View File

@ -1,25 +0,0 @@
language: generic
matrix:
include:
- os: linux
env: CXX=g++-4.9
sudo: required
dist: trusty
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'g++-4.9', 'cmake', 'cmake-data' ]
- os: linux
env: CXX=g++-5
sudo: required
dist: trusty
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'g++-5', 'cmake', 'cmake-data' ]
cache: apt
script:
- cmake . && make && ./cheap_ruler

View File

@ -1,27 +0,0 @@
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
project(cheap_ruler LANGUAGES CXX C)
include(cmake/build.cmake)
include(cmake/mason.cmake)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.10)
set(CMAKE_CONFIGURATION_TYPES Debug Release)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wextra -Wpedantic -Wshadow")
mason_use(geometry VERSION 0.9.2 HEADER_ONLY)
mason_use(gtest VERSION 1.8.0)
mason_use(variant VERSION 1.1.4 HEADER_ONLY)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
add_executable(cheap_ruler
${PROJECT_SOURCE_DIR}/test/cheap_ruler.cpp
)
target_include_directories(cheap_ruler
PUBLIC ${PROJECT_SOURCE_DIR}/include
)
target_add_mason_package(cheap_ruler PRIVATE geometry)
target_add_mason_package(cheap_ruler PRIVATE gtest)
target_add_mason_package(cheap_ruler PRIVATE variant)

View File

@ -1,15 +0,0 @@
ISC License
Copyright (c) 2017, 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.

View File

@ -1,199 +0,0 @@
# cheap-ruler-cpp
Port to C++ of [Cheap Ruler](https://github.com/mapbox/cheap-ruler), a collection of very fast approximations to common geodesic measurements.
[![Build Status](https://travis-ci.org/mapbox/cheap-ruler-cpp.svg?branch=master)](https://travis-ci.org/mapbox/cheap-ruler-cpp)
# Usage
```cpp
#include <mapbox/cheap_ruler.hpp>
namespace cr = mapbox::cheap_ruler;
```
All `point`, `line_string`, `polygon`, and `box` references are [mapbox::geometry](https://github.com/mapbox/geometry.hpp) data structures.
## Create a ruler object
#### `CheapRuler(double latitude, Unit unit)`
Creates a ruler object that will approximate measurements around the given latitude with an optional distance unit. Once created, the ruler object has access to the [methods](#methods) below.
```cpp
auto ruler = cr::CheapRuler(32.8351);
auto milesRuler = cr::CheapRuler(32.8351, cr::CheapRuler::Miles);
```
Possible units:
* `cheap_ruler::CheapRuler::Unit`
* `cheap_ruler::CheapRuler::Kilometers`
* `cheap_ruler::CheapRuler::Miles`
* `cheap_ruler::CheapRuler::NauticalMiles`
* `cheap_ruler::CheapRuler::Meters`
* `cheap_ruler::CheapRuler::Yards`
* `cheap_ruler::CheapRuler::Feet`
* `cheap_ruler::CheapRuler::Inches`
#### `CheapRuler::fromTile(uint32_t y, uint32_t z)`
Creates a ruler object from tile coordinates (`y` and `z` integers). Convenient in tile-reduce scripts.
```cpp
auto ruler = cr::CheapRuler::fromTile(11041, 15);
```
## Methods
#### `distance(point a, point b)`
Given two points of the form [x = longitude, y = latitude], returns the distance (`double`).
```cpp
cr::point point_a{-96.9148, 32.8351};
cr::point point_b{-96.9146, 32.8386};
auto distance = ruler.distance(point_a, point_b);
std::clog << distance; // 0.388595
```
#### `bearing(point a, point b)`
Returns the bearing (`double`) between two points in angles.
```cpp
cr::point point_a{-96.9148, 32.8351};
cr::point point_b{-96.9146, 32.8386};
auto bearing = ruler.bearing(point_a, point_b);
std::clog << bearing; // 2.76206
```
#### `destination(point origin, double distance, double bearing)`
Returns a new point (`point`) given distance and bearing from the starting point.
```cpp
cr::point point_a{-96.9148, 32.8351};
auto dest = ruler.destination(point_a, 1.0, -175);
std::clog << dest.x << ", " << dest.y; // -96.9148, 32.8261
```
#### `offset(point origin, double dx, double dy)`
Returns a new point (`point`) given easting and northing offsets from the starting point.
```cpp
cr::point point_a{-96.9148, 32.8351};
auto os = ruler.offset(point_a, 10.0, -5.0);
std::clog << os.x << ", " << os.y; // -96.808, 32.79
```
#### `lineDistance(const line_string& points)`
Given a line (an array of points), returns the total line distance (`double`).
```cpp
cr::line_string line_a{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }};
auto line_distance = ruler.lineDistance(line_a);
std::clog << line_distance; // 88.2962
```
#### `area(polygon poly)`
Given a polygon (an array of rings, where each ring is an array of points), returns the area (`double`).
```cpp
cr::linear_ring ring{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }, { -96.9, 32.8 }};
auto area = ruler.area(cr::polygon{ ring });
std::clog << area; //
```
#### `along(const line_string& line, double distance)`
Returns the point (`point`) at a specified distance along the line.
```cpp
cr::linear_ring ring{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }, { -96.9, 32.8 }};
auto area = ruler.area(cr::polygon{ ring });
std::clog << area; // 259.581
```
#### `pointOnLine(const line_string& line, point p)`
Returns a tuple of the form `std::pair<point, unsigned>` where point is closest point on the line from the given point, index is the start index of the segment with the closest point, and t is a parameter from 0 to 1 that indicates where the closest point is on that segment.
```cpp
cr::line_string line{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }};
cr::point point{-96.9, 32.79};
auto pol = ruler.pointOnLine(line, point);
auto point = std::get<0>(pol);
std::clog << point.x << ", " << point.y; // -96.9, 32.8 (point)
std::clog << std::get<1>(pol); // 0 (index)
std::clog << std::get<2>(pol); // 0. (t)
```
#### `lineSlice(point start, point stop, const line_string& line)`
Returns a part of the given line (`line_string`) between the start and the stop points (or their closest points on the line).
```cpp
cr::line_string line{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }};
cr::point start_point{-96.9, 32.8};
cr::point stop_point{-96.8, 32.8};
auto slice = ruler.lineSlice(start_point, stop_point, line);
std::clog << slice[0].x << ", " << slice[0].y; // -96.9, 32.8
std::clog << slice[1].x << ", " << slice[1].y; // -96.8, 32.8
```
#### `lineSliceAlong(double start, double stop, const line_string& line)`
Returns a part of the given line (`line_string`) between the start and the stop points indicated by distance along the line.
```cpp
cr::line_string line{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }};
auto slice = ruler.lineSliceAlong(0.1, 1.2, line);
```
#### `bufferPoint(point p, double buffer)`
Given a point, returns a bounding box object ([w, s, e, n]) created from the given point buffered by a given distance.
```cpp
cr::point point{-96.9, 32.8};
auto box = ruler.bufferPoint(point, 0.1);
```
#### `bufferBBox(box bbox, double buffer)`
Given a bounding box, returns the box buffered by a given distance.
```cpp
cr::box bbox({ 30, 38 }, { 40, 39 });
auto bbox2 = ruler.bufferBBox(bbox, 1);
```
#### `insideBBox(point p, box bbox)`
Returns true (`bool`) if the given point is inside in the given bounding box, otherwise false.
```cpp
cr::box bbox({ 30, 38 }, { 40, 39 });
auto inside = ruler.insideBBox({ 35, 38.5 }, bbox);
std::clog << inside; // true
```
# Develop
```shell
# create targets
cmake .
# build
make
# test
./cheap_ruler
# or just do it all in one!
cmake . && make && ./cheap_ruler
```

View File

@ -1,11 +0,0 @@
# Generate source groups so the files are properly sorted in IDEs like Xcode.
function(create_source_groups target)
get_target_property(sources ${target} SOURCES)
foreach(file ${sources})
get_filename_component(file "${file}" ABSOLUTE)
string(REGEX REPLACE "^${CMAKE_SOURCE_DIR}/" "" group "${file}")
get_filename_component(group "${group}" DIRECTORY)
string(REPLACE "/" "\\" group "${group}")
source_group("${group}" FILES "${file}")
endforeach()
endfunction()

View File

@ -1,235 +0,0 @@
# Mason CMake
include(CMakeParseArguments)
function(mason_detect_platform)
# Determine platform
if(NOT MASON_PLATFORM)
# we call uname -s manually here since
# CMAKE_HOST_SYSTEM_NAME will not be defined before the project() call
execute_process(
COMMAND uname -s
OUTPUT_VARIABLE UNAME
OUTPUT_STRIP_TRAILING_WHITESPACE)
if (UNAME STREQUAL "Darwin")
set(MASON_PLATFORM "osx" PARENT_SCOPE)
else()
set(MASON_PLATFORM "linux" PARENT_SCOPE)
endif()
endif()
# Determine platform version string
if(NOT MASON_PLATFORM_VERSION)
# Android Studio only passes ANDROID_ABI, but we need to adjust that to the Mason
if(MASON_PLATFORM STREQUAL "android" AND NOT MASON_PLATFORM_VERSION)
if (ANDROID_ABI STREQUAL "armeabi")
set(MASON_PLATFORM_VERSION "arm-v5-9" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "armeabi-v7a")
set(MASON_PLATFORM_VERSION "arm-v7-9" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "arm64-v8a")
set(MASON_PLATFORM_VERSION "arm-v8-21" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "x86")
set(MASON_PLATFORM_VERSION "x86-9" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "x86_64")
set(MASON_PLATFORM_VERSION "x86-64-21" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "mips")
set(MASON_PLATFORM_VERSION "mips-9" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "mips64")
set(MASON_PLATFORM_VERSION "mips-64-9" PARENT_SCOPE)
else()
message(FATAL_ERROR "Unknown ANDROID_ABI '${ANDROID_ABI}'.")
endif()
elseif(MASON_PLATFORM STREQUAL "ios")
set(MASON_PLATFORM_VERSION "8.0" PARENT_SCOPE)
else()
execute_process(
COMMAND uname -m
OUTPUT_VARIABLE MASON_PLATFORM_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(MASON_PLATFORM_VERSION "${MASON_PLATFORM_VERSION}" PARENT_SCOPE)
endif()
endif()
endfunction()
function(mason_use _PACKAGE)
if(NOT _PACKAGE)
message(FATAL_ERROR "[Mason] No package name given")
endif()
cmake_parse_arguments("" "HEADER_ONLY" "VERSION" "" ${ARGN})
if(_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "[Mason] mason_use() called with unrecognized arguments: ${_UNPARSED_ARGUMENTS}")
endif()
if(NOT _VERSION)
message(FATAL_ERROR "[Mason] Specifying a version is required")
endif()
if(MASON_PACKAGE_${_PACKAGE}_INVOCATION STREQUAL "${MASON_INVOCATION}")
# Check that the previous invocation of mason_use didn't select another version of this package
if(NOT MASON_PACKAGE_${_PACKAGE}_VERSION STREQUAL ${_VERSION})
message(FATAL_ERROR "[Mason] Already using ${_PACKAGE} ${MASON_PACKAGE_${_PACKAGE}_VERSION}. Cannot select version ${_VERSION}.")
endif()
else()
if(_HEADER_ONLY)
set(_PLATFORM_ID "headers")
else()
set(_PLATFORM_ID "${MASON_PLATFORM}-${MASON_PLATFORM_VERSION}")
endif()
set(_SLUG "${_PLATFORM_ID}/${_PACKAGE}/${_VERSION}")
set(_INSTALL_PATH "${MASON_PACKAGE_DIR}/${_SLUG}")
file(RELATIVE_PATH _INSTALL_PATH_RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${_INSTALL_PATH}")
if(NOT EXISTS "${_INSTALL_PATH}")
set(_CACHE_PATH "${MASON_PACKAGE_DIR}/.binaries/${_SLUG}.tar.gz")
if (NOT EXISTS "${_CACHE_PATH}")
# Download the package
set(_URL "${MASON_REPOSITORY}/${_SLUG}.tar.gz")
message("[Mason] Downloading package ${_URL}...")
set(_FAILED)
set(_ERROR)
# Note: some CMake versions are compiled without SSL support
get_filename_component(_CACHE_DIR "${_CACHE_PATH}" DIRECTORY)
file(MAKE_DIRECTORY "${_CACHE_DIR}")
execute_process(
COMMAND curl --retry 3 -s -f -S -L "${_URL}" -o "${_CACHE_PATH}.tmp"
RESULT_VARIABLE _FAILED
ERROR_VARIABLE _ERROR)
if(_FAILED)
message(FATAL_ERROR "[Mason] Failed to download ${_URL}: ${_ERROR}")
else()
# We downloaded to a temporary file to prevent half-finished downloads
file(RENAME "${_CACHE_PATH}.tmp" "${_CACHE_PATH}")
endif()
endif()
# Unpack the package
message("[Mason] Unpacking package to ${_INSTALL_PATH_RELATIVE}...")
file(MAKE_DIRECTORY "${_INSTALL_PATH}")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xzf "${_CACHE_PATH}"
WORKING_DIRECTORY "${_INSTALL_PATH}")
endif()
# Error out if there is no config file.
if(NOT EXISTS "${_INSTALL_PATH}/mason.ini")
message(FATAL_ERROR "[Mason] Could not find mason.ini for package ${_PACKAGE} ${_VERSION}")
endif()
set(MASON_PACKAGE_${_PACKAGE}_PREFIX "${_INSTALL_PATH}" CACHE STRING "${_PACKAGE} ${_INSTALL_PATH}" FORCE)
mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_PREFIX)
# Load the configuration from the ini file
file(STRINGS "${_INSTALL_PATH}/mason.ini" _CONFIG_FILE)
foreach(_LINE IN LISTS _CONFIG_FILE)
string(REGEX MATCH "^([a-z_]+) *= *" _KEY "${_LINE}")
if (_KEY)
string(LENGTH "${_KEY}" _KEY_LENGTH)
string(SUBSTRING "${_LINE}" ${_KEY_LENGTH} -1 _VALUE)
string(REGEX REPLACE ";.*$" "" _VALUE "${_VALUE}") # Trim trailing commas
string(REPLACE "{prefix}" "${_INSTALL_PATH}" _VALUE "${_VALUE}")
string(STRIP "${_VALUE}" _VALUE)
string(REPLACE "=" "" _KEY "${_KEY}")
string(STRIP "${_KEY}" _KEY)
string(TOUPPER "${_KEY}" _KEY)
if(_KEY STREQUAL "INCLUDE_DIRS" OR _KEY STREQUAL "STATIC_LIBS" )
separate_arguments(_VALUE)
endif()
set(MASON_PACKAGE_${_PACKAGE}_${_KEY} "${_VALUE}" CACHE STRING "${_PACKAGE} ${_KEY}" FORCE)
mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_${_KEY})
endif()
endforeach()
# Compare version in the package to catch errors early on
if(NOT _VERSION STREQUAL MASON_PACKAGE_${_PACKAGE}_VERSION)
message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has version '${MASON_PACKAGE_${_PACKAGE}_VERSION}', but required '${_VERSION}'")
endif()
if(NOT _PACKAGE STREQUAL MASON_PACKAGE_${_PACKAGE}_NAME)
message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has name '${MASON_PACKAGE_${_PACKAGE}_NAME}', but required '${_NAME}'")
endif()
if(NOT _HEADER_ONLY)
if(NOT MASON_PLATFORM STREQUAL MASON_PACKAGE_${_PACKAGE}_PLATFORM)
message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has platform '${MASON_PACKAGE_${_PACKAGE}_PLATFORM}', but required '${MASON_PLATFORM}'")
endif()
if(NOT MASON_PLATFORM_VERSION STREQUAL MASON_PACKAGE_${_PACKAGE}_PLATFORM_VERSION)
message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has platform version '${MASON_PACKAGE_${_PACKAGE}_PLATFORM_VERSION}', but required '${MASON_PLATFORM_VERSION}'")
endif()
endif()
# Concatenate the static libs and libraries
set(_LIBRARIES)
list(APPEND _LIBRARIES ${MASON_PACKAGE_${_PACKAGE}_STATIC_LIBS} ${MASON_PACKAGE_${_PACKAGE}_LDFLAGS})
set(MASON_PACKAGE_${_PACKAGE}_LIBRARIES "${_LIBRARIES}" CACHE STRING "${_PACKAGE} _LIBRARIES" FORCE)
mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_LIBRARIES)
if(NOT _HEADER_ONLY)
string(REGEX MATCHALL "(^| +)-L *([^ ]+)" MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LDFLAGS}")
string(REGEX REPLACE "(^| +)-L *" "\\1" MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}")
set(MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}" CACHE STRING "${_PACKAGE} ${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}" FORCE)
mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS)
endif()
# Store invocation ID to prevent different versions of the same package in one invocation
set(MASON_PACKAGE_${_PACKAGE}_INVOCATION "${MASON_INVOCATION}" CACHE INTERNAL "${_PACKAGE} invocation ID" FORCE)
endif()
endfunction()
macro(target_add_mason_package _TARGET _VISIBILITY _PACKAGE)
if (NOT MASON_PACKAGE_${_PACKAGE}_INVOCATION)
message(FATAL_ERROR "[Mason] Package ${_PACKAGE} has not been initialized yet")
endif()
target_include_directories(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_INCLUDE_DIRS}")
target_compile_definitions(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_DEFINITIONS}")
target_compile_options(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_OPTIONS}")
target_link_libraries(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_LIBRARIES}")
endmacro()
# Setup
string(RANDOM LENGTH 16 MASON_INVOCATION)
# Read environment variables if CMake is run in command mode
if (CMAKE_ARGC)
set(MASON_PLATFORM "$ENV{MASON_PLATFORM}")
set(MASON_PLATFORM_VERSION "$ENV{MASON_PLATFORM_VERSION}")
set(MASON_PACKAGE_DIR "$ENV{MASON_PACKAGE_DIR}")
set(MASON_REPOSITORY "$ENV{MASON_REPOSITORY}")
endif()
# Directory where Mason packages are located; typically ends with mason_packages
if (NOT MASON_PACKAGE_DIR)
set(MASON_PACKAGE_DIR "${CMAKE_SOURCE_DIR}/mason_packages")
endif()
# URL prefix of where packages are located.
if (NOT MASON_REPOSITORY)
set(MASON_REPOSITORY "https://mason-binaries.s3.amazonaws.com")
endif()
mason_detect_platform()
# Execute commands if CMake is run in command mode
if (CMAKE_ARGC)
# Collect remaining arguments for passing to mason_use
set(_MASON_ARGS)
foreach(I RANGE 4 ${CMAKE_ARGC})
list(APPEND _MASON_ARGS "${CMAKE_ARGV${I}}")
endforeach()
# Install the package
mason_use(${_MASON_ARGS})
# Optionally print variables
if(DEFINED MASON_PACKAGE_${CMAKE_ARGV4}_${CMAKE_ARGV3})
# CMake can't write to stdout with message()
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${MASON_PACKAGE_${CMAKE_ARGV4}_${CMAKE_ARGV3}}")
endif()
endif()

View File

@ -1,390 +0,0 @@
#pragma once
#include <mapbox/geometry/box.hpp>
#include <mapbox/geometry/multi_line_string.hpp>
#include <mapbox/geometry/polygon.hpp>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <limits>
#include <tuple>
#include <utility>
namespace mapbox {
namespace cheap_ruler {
using box = geometry::box<double>;
using line_string = geometry::line_string<double>;
using linear_ring = geometry::linear_ring<double>;
using multi_line_string = geometry::multi_line_string<double>;
using point = geometry::point<double>;
using polygon = geometry::polygon<double>;
class CheapRuler {
// Values that define WGS84 ellipsoid model of the Earth
static constexpr double RE = 6378.137; // equatorial radius
static constexpr double FE = 1.0 / 298.257223563; // flattening
static constexpr double E2 = FE * (2 - FE);
static constexpr double RAD = M_PI / 180.0;
public:
enum Unit {
Kilometers,
Miles,
NauticalMiles,
Meters,
Metres = Meters,
Yards,
Feet,
Inches
};
//
// A collection of very fast approximations to common geodesic measurements. Useful
// for performance-sensitive code that measures things on a city scale. Point coordinates
// are in the [x = longitude, y = latitude] form.
//
explicit CheapRuler(double latitude, Unit unit = Kilometers) {
double m = 0.;
switch (unit) {
case Kilometers:
m = 1.;
break;
case Miles:
m = 1000. / 1609.344;
break;
case NauticalMiles:
m = 1000. / 1852.;
break;
case Meters:
m = 1000.;
break;
case Yards:
m = 1000. / 0.9144;
break;
case Feet:
m = 1000. / 0.3048;
break;
case Inches:
m = 1000. / 0.0254;
break;
}
// Curvature formulas from https://en.wikipedia.org/wiki/Earth_radius#Meridional
double mul = RAD * RE * m;
double coslat = std::cos(latitude * RAD);
double w2 = 1 / (1 - E2 * (1 - coslat * coslat));
double w = std::sqrt(w2);
// multipliers for converting longitude and latitude degrees into distance
kx = mul * w * coslat; // based on normal radius of curvature
ky = mul * w * w2 * (1 - E2); // based on meridonal radius of curvature
}
static CheapRuler fromTile(uint32_t y, uint32_t z) {
assert(z < 32);
double n = M_PI * (1. - 2. * (y + 0.5) / double(uint32_t(1) << z));
double latitude = std::atan(std::sinh(n)) / RAD;
return CheapRuler(latitude);
}
double squareDistance(point a, point b) const {
auto dx = longDiff(a.x, b.x) * kx;
auto dy = (a.y - b.y) * ky;
return dx * dx + dy * dy;
}
//
// Given two points of the form [x = longitude, y = latitude], returns the distance.
//
double distance(point a, point b) const {
return std::sqrt(squareDistance(a, b));
}
//
// Returns the bearing between two points in angles.
//
double bearing(point a, point b) const {
auto dx = longDiff(b.x, a.x) * kx;
auto dy = (b.y - a.y) * ky;
return std::atan2(dx, dy) / RAD;
}
//
// Returns a new point given distance and bearing from the starting point.
//
point destination(point origin, double dist, double bearing_) const {
auto a = bearing_ * RAD;
return offset(origin, std::sin(a) * dist, std::cos(a) * dist);
}
//
// Returns a new point given easting and northing offsets from the starting point.
//
point offset(point origin, double dx, double dy) const {
return point(origin.x + dx / kx, origin.y + dy / ky);
}
//
// Given a line (an array of points), returns the total line distance.
//
double lineDistance(const line_string& points) {
double total = 0.;
for (size_t i = 1; i < points.size(); ++i) {
total += distance(points[i - 1], points[i]);
}
return total;
}
//
// Given a polygon (an array of rings, where each ring is an array of points),
// returns the area.
//
double area(polygon poly) const {
double sum = 0.;
for (unsigned i = 0; i < poly.size(); ++i) {
auto& ring = poly[i];
for (unsigned j = 0, len = ring.size(), k = len - 1; j < len; k = j++) {
sum += longDiff(ring[j].x, ring[k].x) *
(ring[j].y + ring[k].y) * (i ? -1. : 1.);
}
}
return (std::abs(sum) / 2.) * kx * ky;
}
//
// Returns the point at a specified distance along the line.
//
point along(const line_string& line, double dist) const {
double sum = 0.;
if (line.empty()) {
return {};
}
if (dist <= 0.) {
return line[0];
}
for (unsigned i = 0; i < line.size() - 1; ++i) {
auto p0 = line[i];
auto p1 = line[i + 1];
auto d = distance(p0, p1);
sum += d;
if (sum > dist) {
return interpolate(p0, p1, (dist - (sum - d)) / d);
}
}
return line[line.size() - 1];
}
//
// Returns the distance from a point `p` to a line segment `a` to `b`.
//
double pointToSegmentDistance(const point& p, const point& a, const point& b) const {
auto t = 0.0;
auto x = a.x;
auto y = a.y;
auto dx = longDiff(b.x, x) * kx;
auto dy = (b.y - y) * ky;
if (dx != 0.0 || dy != 0.0) {
t = (longDiff(p.x, x) * kx * dx + (p.y - y) * ky * dy) / (dx * dx + dy * dy);
if (t > 1.0) {
x = b.x;
y = b.y;
} else if (t > 0.0) {
x += (dx / kx) * t;
y += (dy / ky) * t;
}
}
return distance(p, { x, y });
}
//
// Returns a tuple of the form <point, index, t> where point is closest point on the line
// from the given point, index is the start index of the segment with the closest point,
// and t is a parameter from 0 to 1 that indicates where the closest point is on that segment.
//
std::tuple<point, unsigned, double> pointOnLine(const line_string& line, point p) const {
double minDist = std::numeric_limits<double>::infinity();
double minX = 0., minY = 0., minI = 0., minT = 0.;
if (line.empty()) {
return std::make_tuple(point(), 0., 0.);
}
for (unsigned i = 0; i < line.size() - 1; ++i) {
auto t = 0.;
auto x = line[i].x;
auto y = line[i].y;
auto dx = longDiff(line[i + 1].x, x) * kx;
auto dy = (line[i + 1].y - y) * ky;
if (dx != 0. || dy != 0.) {
t = (longDiff(p.x, x) * kx * dx +
(p.y - y) * ky * dy) / (dx * dx + dy * dy);
if (t > 1) {
x = line[i + 1].x;
y = line[i + 1].y;
} else if (t > 0) {
x += (dx / kx) * t;
y += (dy / ky) * t;
}
}
auto sqDist = squareDistance(p, {x, y});
if (sqDist < minDist) {
minDist = sqDist;
minX = x;
minY = y;
minI = i;
minT = t;
}
}
return std::make_tuple(
point(minX, minY), minI, ::fmax(0., ::fmin(1., minT)));
}
//
// Returns a part of the given line between the start and the stop points (or their closest
// points on the line).
//
line_string lineSlice(point start, point stop, const line_string& line) const {
auto getPoint = [](auto tuple) { return std::get<0>(tuple); };
auto getIndex = [](auto tuple) { return std::get<1>(tuple); };
auto getT = [](auto tuple) { return std::get<2>(tuple); };
auto p1 = pointOnLine(line, start);
auto p2 = pointOnLine(line, stop);
if (getIndex(p1) > getIndex(p2) || (getIndex(p1) == getIndex(p2) && getT(p1) > getT(p2))) {
auto tmp = p1;
p1 = p2;
p2 = tmp;
}
line_string slice = { getPoint(p1) };
auto l = getIndex(p1) + 1;
auto r = getIndex(p2);
if (line[l] != slice[0] && l <= r) {
slice.push_back(line[l]);
}
for (unsigned i = l + 1; i <= r; ++i) {
slice.push_back(line[i]);
}
if (line[r] != getPoint(p2)) {
slice.push_back(getPoint(p2));
}
return slice;
};
//
// Returns a part of the given line between the start and the stop points
// indicated by distance along the line.
//
line_string lineSliceAlong(double start, double stop, const line_string& line) const {
double sum = 0.;
line_string slice;
for (size_t i = 1; i < line.size(); ++i) {
auto p0 = line[i - 1];
auto p1 = line[i];
auto d = distance(p0, p1);
sum += d;
if (sum > start && slice.size() == 0) {
slice.push_back(interpolate(p0, p1, (start - (sum - d)) / d));
}
if (sum >= stop) {
slice.push_back(interpolate(p0, p1, (stop - (sum - d)) / d));
return slice;
}
if (sum > start) {
slice.push_back(p1);
}
}
return slice;
};
//
// Given a point, returns a bounding box object ([w, s, e, n])
// created from the given point buffered by a given distance.
//
box bufferPoint(point p, double buffer) const {
auto v = buffer / ky;
auto h = buffer / kx;
return box(
point(p.x - h, p.y - v),
point(p.x + h, p.y + v)
);
}
//
// Given a bounding box, returns the box buffered by a given distance.
//
box bufferBBox(box bbox, double buffer) const {
auto v = buffer / ky;
auto h = buffer / kx;
return box(
point(bbox.min.x - h, bbox.min.y - v),
point(bbox.max.x + h, bbox.max.y + v)
);
}
//
// Returns true if the given point is inside in the given bounding box, otherwise false.
//
static bool insideBBox(point p, box bbox) {
return p.y >= bbox.min.y &&
p.y <= bbox.max.y &&
longDiff(p.x, bbox.min.x) >= 0 &&
longDiff(p.x, bbox.max.x) <= 0;
}
static point interpolate(point a, point b, double t) {
double dx = longDiff(b.x, a.x);
double dy = b.y - a.y;
return point(a.x + dx * t, a.y + dy * t);
}
private:
double ky;
double kx;
static double longDiff(double a, double b) {
return std::remainder(a - b, 360);
}
};
} // namespace cheap_ruler
} // namespace mapbox

View File

@ -1,276 +0,0 @@
#include <mapbox/cheap_ruler.hpp>
#include <gtest/gtest.h>
#include <random>
#include "fixtures/lines.hpp"
#include "fixtures/turf.hpp"
namespace cr = mapbox::cheap_ruler;
class CheapRulerTest : public ::testing::Test {
protected:
cr::CheapRuler ruler = cr::CheapRuler(32.8351);
cr::CheapRuler milesRuler = cr::CheapRuler(32.8351, cr::CheapRuler::Miles);
};
void assertErr(double expected, double actual, double maxError) {
// Add a negligible fraction to make sure we
// don't divide by zero.
double error = std::abs((actual - expected) /
(expected == 0. ? expected + 0.000001 : expected));
if (error > maxError) {
FAIL() << "expected is " << expected << " but got " << actual;
}
}
TEST_F(CheapRulerTest, distance) {
for (unsigned i = 0; i < points.size() - 1; ++i) {
auto expected = turf_distance[i];
auto actual = ruler.distance(points[i], points[i + 1]);
assertErr(expected, actual, .003);
}
}
TEST_F(CheapRulerTest, distanceInMiles) {
auto d = ruler.distance({ 30.5, 32.8351 }, { 30.51, 32.8451 });
auto d2 = milesRuler.distance({ 30.5, 32.8351 }, { 30.51, 32.8451 });
assertErr(d / d2, 1.609344, 1e-12);
}
TEST_F(CheapRulerTest, bearing) {
for (unsigned i = 0; i < points.size() - 1; ++i) {
auto expected = turf_bearing[i];
auto actual = ruler.bearing(points[i], points[i + 1]);
assertErr(expected, actual, .005);
}
}
TEST_F(CheapRulerTest, destination) {
for (unsigned i = 0; i < points.size(); ++i) {
auto bearing = (i % 360) - 180.;
auto expected = turf_destination[i];
auto actual = ruler.destination(points[i], 1.0, bearing);
assertErr(expected.x, actual.x, 1e-6); // longitude
assertErr(expected.y, actual.y, 1e-6); // latitude
}
}
TEST_F(CheapRulerTest, lineDistance) {
{
cr::line_string emptyLine {};
auto expected = 0.0;
auto actual = ruler.lineDistance(emptyLine);
assertErr(expected, actual, 0.0);
}
for (unsigned i = 0; i < lines.size(); ++i) {
auto expected = turf_lineDistance[i];
auto actual = ruler.lineDistance(lines[i]);
assertErr(expected, actual, 0.003);
}
}
TEST_F(CheapRulerTest, area) {
for (unsigned i = 0, j = 0; i < lines.size(); ++i) {
if (lines[i].size() < 3) {
continue;
}
cr::linear_ring ring;
for (auto point : lines[i]) {
ring.push_back(point);
}
ring.push_back(lines[i][0]);
auto expected = turf_area[j++];
auto actual = ruler.area(cr::polygon{ ring });
assertErr(expected, actual, 0.003);
}
}
TEST_F(CheapRulerTest, along) {
{
cr::point emptyPoint {};
cr::line_string emptyLine {};
auto expected = emptyPoint;
auto actual = ruler.along(emptyLine, 0.0);
assertErr(expected.x, actual.x, 0.0);
assertErr(expected.y, actual.y, 0.0);
}
for (unsigned i = 0; i < lines.size(); ++i) {
auto expected = turf_along[i];
auto actual = ruler.along(lines[i], turf_along_dist[i]);
assertErr(expected.x, actual.x, 1e-6); // along longitude
assertErr(expected.y, actual.y, 1e-6); // along latitude
}
}
TEST_F(CheapRulerTest, alongWithDist) {
ASSERT_EQ(ruler.along(lines[0], -5), lines[0][0]);
}
TEST_F(CheapRulerTest, alongWithDistGreaterThanLength) {
ASSERT_EQ(ruler.along(lines[0], 1000), lines[0][lines[0].size() - 1]);
}
TEST_F(CheapRulerTest, pointOnLine) {
// not Turf comparison because pointOnLine is bugged https://github.com/Turfjs/turf/issues/344
cr::line_string line = {{ -77.031669, 38.878605 }, { -77.029609, 38.881946 }};
auto result = ruler.pointOnLine(line, { -77.034076, 38.882017 });
assertErr(std::get<0>(result).x, -77.03052689033436, 1e-6);
assertErr(std::get<0>(result).y, 38.880457324462576, 1e-6);
ASSERT_EQ(std::get<1>(result), 0u); // index
assertErr(std::get<2>(result), 0.5544221677861756, 1e-6); // t
ASSERT_EQ(std::get<2>(ruler.pointOnLine(line, { -80., 38. })), 0.) << "t is not less than 0";
ASSERT_EQ(std::get<2>(ruler.pointOnLine(line, { -75., 38. })), 1.) << "t is not bigger than 1";
}
TEST_F(CheapRulerTest, pointToSegmentDistance) {
cr::point p{ -77.034076, 38.882017 };
cr::point p0{ -77.031669, 38.878605 };
cr::point p1{ -77.029609, 38.881946 };
const auto distance = ruler.pointToSegmentDistance(p, p0, p1);
assertErr(0.37461484020420416, distance, 1e-6);
}
TEST_F(CheapRulerTest, lineSlice) {
for (unsigned i = 0; i < lines.size(); ++i) {
auto line = lines[i];
auto dist = ruler.lineDistance(line);
auto start = ruler.along(line, dist * 0.3);
auto stop = ruler.along(line, dist * 0.7);
auto expected = turf_lineSlice[i];
auto actual = ruler.lineDistance(ruler.lineSlice(start, stop, line));
/// @todo Should update turf_lineSlice and revert maxError back.
assertErr(expected, actual, 1e-4);
}
}
TEST_F(CheapRulerTest, lineSliceAlong) {
{
cr::line_string emptyLine {};
auto expected = ruler.lineDistance(emptyLine);
auto actual = ruler.lineDistance(ruler.lineSliceAlong(0.0, 0.0, emptyLine));
assertErr(expected, actual, 0.0);
}
for (unsigned i = 0; i < lines.size(); ++i) {
if (i == 46) {
// skip due to Turf bug https://github.com/Turfjs/turf/issues/351
continue;
};
auto line = lines[i];
auto dist = ruler.lineDistance(line);
auto expected = turf_lineSlice[i];
auto actual = ruler.lineDistance(ruler.lineSliceAlong(dist * 0.3, dist * 0.7, line));
/// @todo Should update turf_lineSlice and revert maxError back.
assertErr(expected, actual, 1e-4);
}
}
TEST_F(CheapRulerTest, lineSliceReverse) {
auto line = lines[0];
auto dist = ruler.lineDistance(line);
auto start = ruler.along(line, dist * 0.7);
auto stop = ruler.along(line, dist * 0.3);
auto actual = ruler.lineDistance(ruler.lineSlice(start, stop, line));
assertErr(0.018676476689649835, actual, 1e-6);
}
TEST_F(CheapRulerTest, bufferPoint) {
for (unsigned i = 0; i < points.size(); ++i) {
auto expected = turf_bufferPoint[i];
auto actual = milesRuler.bufferPoint(points[i], 0.1);
assertErr(expected.min.x, actual.min.x, 2e-7);
assertErr(expected.min.x, actual.min.x, 2e-7);
assertErr(expected.max.y, actual.max.y, 2e-7);
assertErr(expected.max.y, actual.max.y, 2e-7);
}
}
TEST_F(CheapRulerTest, bufferBBox) {
cr::box bbox({ 30, 38 }, { 40, 39 });
cr::box bbox2 = ruler.bufferBBox(bbox, 1);
assertErr(bbox2.min.x, 29.989319515875376, 1e-6);
assertErr(bbox2.min.y, 37.99098271225711, 1e-6);
assertErr(bbox2.max.x, 40.01068048412462, 1e-6);
assertErr(bbox2.max.y, 39.00901728774289, 1e-6);
}
TEST_F(CheapRulerTest, insideBBox) {
cr::box bbox({ 30, 38 }, { 40, 39 });
ASSERT_TRUE(ruler.insideBBox({ 35, 38.5 }, bbox));
ASSERT_FALSE(ruler.insideBBox({ 45, 45 }, bbox));
}
TEST_F(CheapRulerTest, fromTile) {
auto ruler1 = cr::CheapRuler(50.5);
auto ruler2 = cr::CheapRuler::fromTile(11041, 15);
cr::point p1(30.5, 50.5);
cr::point p2(30.51, 50.51);
assertErr(ruler1.distance(p1, p2), ruler2.distance(p1, p2), 2e-5);
}
TEST_F(CheapRulerTest, longitudeWrap) {
std::random_device rd;
std::mt19937 gen(rd());
std::bernoulli_distribution d(0.5); // true with prob 0.5
auto r = cr::CheapRuler(50.5);
cr::polygon poly(1);
auto& ring = poly[0];
cr::line_string line;
cr::point origin(0, 50.5); // Greenwich
auto rad = 1000.0;
// construct a regular dodecagon
for (int i = -180; i <= 180; i += 30) {
auto p = r.destination(origin, rad, i);
// shift randomly east/west to the international date line
p.x += d(gen) ? 180 : -180;
ring.push_back(p);
line.push_back(p);
}
auto p = r.lineDistance(line);
auto a = r.area(poly);
// cheap_ruler does planar calculations, so the perimeter and area of a
// planar regular dodecagon with circumradius rad are used in these checks.
// For the record, the results for rad = 1000 km are:
// perimeter area
// planar 6211.657082 3000000
// WGS84 6187.959236 2996317.6328
// error 0.38% 0.12%
assertErr(12 * rad / sqrt(2 + sqrt(3.0)), p, 1e-12);
assertErr(3 * rad * rad, a, 1e-12);
for (int j = 1; j < (int)line.size(); ++j) {
auto azi = r.bearing(line[j-1], line[j]);
// offset expect and actual by 1 to make err criterion absolute
assertErr(1, std::remainder(270 - 15 + 30*j - azi, 360) + 1, 1e-12);
}
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
mason_packages
test

View File

@ -1,3 +0,0 @@
[submodule ".mason"]
path = .mason
url = https://github.com/mapbox/mason.git

View File

@ -1,50 +0,0 @@
language: generic
sudo: false
matrix:
include:
- os: linux
env: CXX=g++-4.9
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'g++-4.9' ]
- os: linux
env: CXX=g++-5
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'g++-5' ]
- os: linux
env: CXX=g++-6
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'g++-6' ]
- os: linux
env: CXX=clang++-3.8
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'libstdc++-4.9-dev' ]
before_script:
- git submodule update --init
- .mason/mason install clang++ 3.8.1
- export PATH=$(.mason/mason prefix clang++ 3.8.1)/bin:$PATH
- os: linux
env: CXX=clang++-3.9
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'libstdc++-4.9-dev' ]
before_script:
- git submodule update --init
- .mason/mason install clang++ 3.9.1
- export PATH=$(.mason/mason prefix clang++ 3.9.1)/bin:$PATH
- os: osx
osx_image: xcode7.3
cache: apt
script:
- make test

View File

@ -1,13 +0,0 @@
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.

View File

@ -1,19 +0,0 @@
CXXFLAGS += -I include -std=c++14 -DDEBUG -O0 -Wall -Wextra -Werror
MASON ?= .mason/mason
VARIANT = 1.1.4
default: test
$(MASON):
git submodule update --init
mason_packages/headers/variant/$(VARIANT):
$(MASON) install variant $(VARIANT)
test: tests/* include/mapbox/geometry/* mason_packages/headers/variant/$(VARIANT) Makefile
$(CXX) tests/*.cpp $(CXXFLAGS) `$(MASON) cflags variant $(VARIANT)` -o test
./test
clean:
rm -f test

View File

@ -1,83 +0,0 @@
# geometry.hpp
Provides header-only, generic C++ interfaces for geometry types, geometry collections, and features.
- `mapbox::geometry::point`
- `mapbox::geometry::multi_point`
- `mapbox::geometry::line_string`
- `mapbox::geometry::multi_line_string`
- `mapbox::geometry::polygon`
- `mapbox::geometry::multi_polygon`
- `mapbox::geometry::geometry_collection`
- `mapbox::geometry::feature` (experimental)
### Design
These types are designed to be easy to parse and serialize to [GeoJSON](http://geojson.org/).
They should also be a robust and high performance container for data processing and conversion.
### Goals
- Header-only
- Fast compile
- c++11/c++14 compatibility
- No external dependencies for usage of core types (point, line_string, etc)
- Minimal dependencies for usage of enclosing `geometry` type (`mapbox::variant`)
- Easily [adaptable to `boost::geometry`](http://www.boost.org/doc/libs/1_56_0/libs/geometry/doc/html/geometry/examples/example__adapting_a_legacy_geometry_object_model.html)
### Usage
Using a single type directly (requires no external dependencies):
```cpp
#include <mapbox/geometry/point.hpp>
#include <iostream>
using mapbox::geometry::point;
int main() {
point<double> pt(1.0,0.0);
std::clog << "x: " << pt.x << " y: " << pt.y << "\n";
}
```
Creating a geometry collection (depends on https://github.com/mapbox/variant):
```cpp
#include <mapbox/geometry/geometry.hpp>
#include <mapbox/variant.hpp>
#include <iostream>
using mapbox::geometry::geometry_collection;
using mapbox::geometry::geometry;
using mapbox::geometry::point;
using point_type = point<double>;
struct printer
{
printer() {}
void operator()(point_type const& pt) const
{
std::clog << "x: " << pt.x << " y: " << pt.y << "\n";
}
template <typename T>
void operator()(T const& g) const
{
std::clog << "encountered non-point geometry\n";
}
};
int main() {
geometry_collection<double> gc;
gc.emplace_back(point_type(1.0,0.0));
geometry<double> const& geom = gc.at(0);
printer visitor;
mapbox::util::apply_visitor(visitor,geom);
}
```

View File

@ -1,13 +0,0 @@
#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/feature.hpp>
#include <mapbox/geometry/point_arithmetic.hpp>
#include <mapbox/geometry/for_each_point.hpp>
#include <mapbox/geometry/envelope.hpp>

View File

@ -1,34 +0,0 @@
#pragma once
#include <mapbox/geometry/point.hpp>
namespace mapbox {
namespace geometry {
template <typename T>
struct box
{
using point_type = point<T>;
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

View File

@ -1,33 +0,0 @@
#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

View File

@ -1,91 +0,0 @@
#pragma once
#include <mapbox/geometry/geometry.hpp>
#include <mapbox/variant.hpp>
#include <cstdint>
#include <string>
#include <vector>
#include <unordered_map>
#include <mapbox/optional.hpp>
namespace mapbox {
namespace geometry {
struct value;
struct null_value_t
{
constexpr null_value_t() {}
constexpr null_value_t(std::nullptr_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<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 {};
mapbox::util::optional<identifier> id {};
// GCC 4.9 does not support C++14 aggregates with non-static data member
// initializers.
feature(geometry_type geometry_,
property_map properties_ = property_map {},
mapbox::util::optional<identifier> id_ = mapbox::util::optional<identifier> {})
: geometry(std::move(geometry_)),
properties(std::move(properties_)),
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 container_type::container_type;
};
} // namespace geometry
} // namespace mapbox

View File

@ -1,45 +0,0 @@
#pragma once
#include <mapbox/geometry/geometry.hpp>
namespace mapbox {
namespace geometry {
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

View File

@ -1,58 +0,0 @@
#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/variant.hpp>
// stl
#include <vector>
namespace mapbox {
namespace geometry {
template <typename T, template <typename...> class Cont = std::vector>
struct geometry_collection;
template <typename T>
using geometry_base = mapbox::util::variant<point<T>,
line_string<T>,
polygon<T>,
multi_point<T>,
multi_line_string<T>,
multi_polygon<T>,
geometry_collection<T>>;
template <typename T>
struct geometry : geometry_base<T>
{
using coordinate_type = T;
using geometry_base<T>::geometry_base;
/*
* The default constructor would create a point geometry with default-constructed coordinates;
* i.e. (0, 0). Since this is not particularly useful, and could hide bugs, it is disabled.
*/
geometry() = delete;
};
template <typename T, template <typename...> class Cont>
struct geometry_collection : Cont<geometry<T>>
{
using coordinate_type = T;
using geometry_type = geometry<T>;
using container_type = Cont<geometry_type>;
geometry_collection() = default;
geometry_collection(geometry_collection const&) = default;
geometry_collection(geometry_collection &&) = default;
geometry_collection(std::initializer_list<geometry_type> && args)
: container_type(std::forward<std::initializer_list<geometry_type>>(args)) {};
};
} // namespace geometry
} // namespace mapbox

View File

@ -1,21 +0,0 @@
#pragma once
// mapbox
#include <mapbox/geometry/point.hpp>
// stl
#include <vector>
namespace mapbox {
namespace geometry {
template <typename T, template <typename...> class Cont = std::vector>
struct line_string : Cont<point<T> >
{
using coordinate_type = T;
using point_type = point<T>;
using container_type = Cont<point_type>;
using container_type::container_type;
};
} // namespace geometry
} // namespace mapbox

View File

@ -1,21 +0,0 @@
#pragma once
// mapbox
#include <mapbox/geometry/line_string.hpp>
// stl
#include <vector>
namespace mapbox {
namespace geometry {
template <typename T, template <typename...> class Cont = std::vector>
struct multi_line_string : Cont<line_string<T>>
{
using coordinate_type = T;
using line_string_type = line_string<T>;
using container_type = Cont<line_string_type>;
using container_type::container_type;
};
} // namespace geometry
} // namespace mapbox

View File

@ -1,21 +0,0 @@
#pragma once
// mapbox
#include <mapbox/geometry/point.hpp>
// stl
#include <vector>
namespace mapbox {
namespace geometry {
template <typename T, template <typename...> class Cont = std::vector>
struct multi_point : Cont<point<T>>
{
using coordinate_type = T;
using point_type = point<T>;
using container_type = Cont<point_type>;
using container_type::container_type;
};
} // namespace geometry
} // namespace mapbox

View File

@ -1,21 +0,0 @@
#pragma once
// mapbox
#include <mapbox/geometry/polygon.hpp>
// stl
#include <vector>
namespace mapbox {
namespace geometry {
template <typename T, template <typename...> class Cont = std::vector>
struct multi_polygon : Cont<polygon<T>>
{
using coordinate_type = T;
using polygon_type = polygon<T>;
using container_type = Cont<polygon_type>;
using container_type::container_type;
};
} // namespace geometry
} // namespace mapbox

View File

@ -1,35 +0,0 @@
#pragma once
namespace mapbox {
namespace geometry {
template <typename T>
struct point
{
using coordinate_type = T;
constexpr point()
: x(), y()
{}
constexpr point(T x_, T y_)
: x(x_), y(y_)
{}
T x;
T y;
};
template <typename T>
constexpr bool operator==(point<T> const& lhs, point<T> const& rhs)
{
return lhs.x == rhs.x && lhs.y == rhs.y;
}
template <typename T>
constexpr bool operator!=(point<T> const& lhs, point<T> const& rhs)
{
return !(lhs == rhs);
}
} // namespace geometry
} // namespace mapbox

View File

@ -1,119 +0,0 @@
#pragma once
namespace mapbox {
namespace geometry {
template <typename T>
point<T> operator+(point<T> const& lhs, point<T> const& rhs)
{
return point<T>(lhs.x + rhs.x, lhs.y + rhs.y);
}
template <typename T>
point<T> operator+(point<T> const& lhs, T const& rhs)
{
return point<T>(lhs.x + rhs, lhs.y + rhs);
}
template <typename T>
point<T> operator-(point<T> const& lhs, point<T> const& rhs)
{
return point<T>(lhs.x - rhs.x, lhs.y - rhs.y);
}
template <typename T>
point<T> operator-(point<T> const& lhs, T const& rhs)
{
return point<T>(lhs.x - rhs, lhs.y - rhs);
}
template <typename T>
point<T> operator*(point<T> const& lhs, point<T> const& rhs)
{
return point<T>(lhs.x * rhs.x, lhs.y * rhs.y);
}
template <typename T>
point<T> operator*(point<T> const& lhs, T const& rhs)
{
return point<T>(lhs.x * rhs, lhs.y * rhs);
}
template <typename T>
point<T> operator/(point<T> const& lhs, point<T> const& rhs)
{
return point<T>(lhs.x / rhs.x, lhs.y / rhs.y);
}
template <typename T>
point<T> operator/(point<T> const& lhs, T const& rhs)
{
return point<T>(lhs.x / rhs, lhs.y / rhs);
}
template <typename T>
point<T>& operator+=(point<T>& lhs, point<T> const& rhs)
{
lhs.x += rhs.x;
lhs.y += rhs.y;
return lhs;
}
template <typename T>
point<T>& operator+=(point<T>& lhs, T const& rhs)
{
lhs.x += rhs;
lhs.y += rhs;
return lhs;
}
template <typename T>
point<T>& operator-=(point<T>& lhs, point<T> const& rhs)
{
lhs.x -= rhs.x;
lhs.y -= rhs.y;
return lhs;
}
template <typename T>
point<T>& operator-=(point<T>& lhs, T const& rhs)
{
lhs.x -= rhs;
lhs.y -= rhs;
return lhs;
}
template <typename T>
point<T>& operator*=(point<T>& lhs, point<T> const& rhs)
{
lhs.x *= rhs.x;
lhs.y *= rhs.y;
return lhs;
}
template <typename T>
point<T>& operator*=(point<T>& lhs, T const& rhs)
{
lhs.x *= rhs;
lhs.y *= rhs;
return lhs;
}
template <typename T>
point<T>& operator/=(point<T>& lhs, point<T> const& rhs)
{
lhs.x /= rhs.x;
lhs.y /= rhs.y;
return lhs;
}
template <typename T>
point<T>& operator/=(point<T>& lhs, T const& rhs)
{
lhs.x /= rhs;
lhs.y /= rhs;
return lhs;
}
} // namespace geometry
} // namespace mapbox

View File

@ -1,31 +0,0 @@
#pragma once
// mapbox
#include <mapbox/geometry/point.hpp>
// stl
#include <vector>
namespace mapbox {
namespace geometry {
template <typename T, template <typename...> class Cont = std::vector>
struct linear_ring : Cont<point<T>>
{
using coordinate_type = T;
using point_type = point<T>;
using container_type = Cont<point_type>;
using container_type::container_type;
};
template <typename T, template <typename...> class Cont = std::vector>
struct polygon : Cont<linear_ring<T>>
{
using coordinate_type = T;
using linear_ring_type = linear_ring<T>;
using container_type = Cont<linear_ring_type>;
using container_type::container_type;
};
} // namespace geometry
} // namespace mapbox

View File

@ -1,5 +0,0 @@
#include <mapbox/geometry.hpp>
void test() {
mapbox::geometry::geometry_collection<double> gc;
}

View File

@ -1,260 +0,0 @@
#include <mapbox/geometry.hpp>
#include <cassert>
using namespace mapbox::geometry;
static void testPoint() {
point<double> p1;
assert(int(p1.x) == 0);
assert(int(p1.y) == 0);
point<uint32_t> p2(2, 3);
point<uint32_t> p3(4, 6);
assert((p2 + p3) == point<uint32_t>(6, 9));
assert((p2 + 1u) == point<uint32_t>(3, 4));
assert((p3 - p2) == point<uint32_t>(2, 3));
assert((p3 - 1u) == point<uint32_t>(3, 5));
assert((p3 * p2) == point<uint32_t>(8, 18));
assert((p2 * 2u) == point<uint32_t>(4, 6));
assert((p3 / p2) == point<uint32_t>(2, 2));
assert((p3 / 2u) == point<uint32_t>(2, 3));
{ point<uint32_t> p(2, 3); assert((p += p3) == point<uint32_t>(6, 9)); }
{ point<uint32_t> p(2, 3); assert((p += 1u) == point<uint32_t>(3, 4)); }
{ point<uint32_t> p(4, 6); assert((p -= p2) == point<uint32_t>(2, 3)); }
{ point<uint32_t> p(4, 6); assert((p -= 1u) == point<uint32_t>(3, 5)); }
{ point<uint32_t> p(4, 6); assert((p *= p2) == point<uint32_t>(8, 18)); }
{ point<uint32_t> p(2, 3); assert((p *= 2u) == point<uint32_t>(4, 6)); }
{ point<uint32_t> p(4, 6); assert((p /= p2) == point<uint32_t>(2, 2)); }
{ point<uint32_t> p(4, 6); assert((p /= 2u) == point<uint32_t>(2, 3)); }
}
static void testMultiPoint() {
multi_point<double> mp1;
assert(mp1.size() == 0);
multi_point<double> mp2(10);
assert(mp2.size() == 10);
assert(mp1 == mp1);
assert(!(mp1 != mp1));
assert(mp1 != mp2);
}
static void testLineString() {
line_string<double> ls1;
assert(ls1.size() == 0);
line_string<double> ls2(10);
assert(ls2.size() == 10);
assert(ls1 == ls1);
assert(!(ls1 != ls1));
assert(ls1 != ls2);
}
static void testMultiLineString() {
multi_line_string<double> mls1;
assert(mls1.size() == 0);
multi_line_string<double> mls2(10);
assert(mls2.size() == 10);
assert(mls1 == mls1);
assert(!(mls1 != mls1));
assert(mls1 != mls2);
}
static void testPolygon() {
polygon<double> pg1;
assert(pg1.size() == 0);
polygon<double> pg2({{{0, 1}}});
assert(pg2.size() == 1);
assert(pg2[0].size() == 1);
assert(pg2[0][0] == point<double>(0, 1));
assert(pg1 == pg1);
assert(!(pg1 != pg1));
assert(pg1 != pg2);
}
static void testMultiPolygon() {
multi_polygon<double> mpg1;
assert(mpg1.size() == 0);
multi_polygon<double> mpg2(10);
assert(mpg2.size() == 10);
assert(mpg1 == mpg1);
assert(!(mpg1 != mpg1));
assert(mpg1 != mpg2);
}
static void testGeometry() {
geometry<double> pg { point<double>() };
assert(pg.is<point<double>>());
geometry<double> lsg { line_string<double>() };
assert(lsg.is<line_string<double>>());
geometry<double> pgg { polygon<double>() };
assert(pgg.is<polygon<double>>());
geometry<double> mpg { multi_point<double>() };
assert(mpg.is<multi_point<double>>());
geometry<double> mlsg { multi_line_string<double>() };
assert(mlsg.is<multi_line_string<double>>());
geometry<double> mpgg { multi_polygon<double>() };
assert(mpgg.is<multi_polygon<double>>());
geometry<double> gcg { geometry_collection<double>() };
assert(gcg.is<geometry_collection<double>>());
assert(pg == pg);
assert(!(pg != pg));
assert(pg != lsg);
}
static void testGeometryCollection() {
geometry_collection<double> gc1;
assert(gc1.size() == 0);
assert(gc1 == gc1);
assert(!(gc1 != gc1));
}
static void testFeature() {
feature<double> pf { point<double>() };
assert(pf.geometry.is<point<double>>());
assert(pf.properties.size() == 0);
auto &p = pf.properties;
p["bool"] = true;
p["string"] = std::string("foo");
p["double"] = 2.5;
p["uint"] = uint64_t(10);
p["int"] = int64_t(-10);
p["null"] = null_value;
assert(p["bool"].is<bool>());
assert(p["bool"] == true);
assert(p["string"].is<std::string>());
assert(p["string"] == std::string("foo"));
assert(p["double"].is<double>());
assert(p["double"] == 2.5);
assert(p["uint"].is<uint64_t>());
assert(p["uint"] == uint64_t(10));
assert(p["int"].is<int64_t>());
assert(p["int"] == int64_t(-10));
assert(p["null"].is<null_value_t>());
assert(p["null"] == null_value);
p["null"] = null_value_t{};
assert(p["null"].is<null_value_t>());
assert(p["null"] == null_value);
assert(p == p);
assert(!(p != p));
assert(pf == pf);
assert(!(pf != pf));
assert(p.size() == 6);
feature<double> id1 { point<double>() };
id1.id = { uint64_t(1) };
feature<double> id2 { point<double>() };
id1.id = { uint64_t(2) };
assert(id1 == id1);
assert(id1 != id2);
}
static void testFeatureCollection() {
feature_collection<double> fc1;
assert(fc1.size() == 0);
assert(fc1 == fc1);
assert(!(fc1 != fc1));
}
struct point_counter {
std::size_t count = 0;
template <class Point>
void operator()(Point const&) { count++; };
};
static void testForEachPoint() {
auto count_points = [] (auto const& g) {
point_counter counter;
for_each_point(g, counter);
return counter.count;
};
assert(count_points(point<double>()) == 1);
assert(count_points(line_string<double>({{0, 1}, {2, 3}})) == 2);
assert(count_points(geometry<double>(polygon<double>({{{0, 1}, {2, 3}}}))) == 2);
auto point_negator = [] (point<double>& p) { p *= -1.0; };
point<double> p(1, 2);
for_each_point(p, point_negator);
assert(p == point<double>(-1, -2));
line_string<double> ls({{0, 1}, {2, 3}});
for_each_point(ls, point_negator);
assert(ls == line_string<double>({{0, -1}, {-2, -3}}));
geometry<double> g(polygon<double>({{{0, 1}, {2, 3}}}));
for_each_point(g, point_negator);
assert(g == geometry<double>(polygon<double>({{{0, -1}, {-2, -3}}})));
// Custom geometry type
using my_geometry = mapbox::util::variant<point<double>>;
assert(count_points(my_geometry(point<double>())) == 1);
// Custom point type
struct my_point {
int16_t x;
int16_t y;
};
assert(count_points(std::vector<my_point>({my_point{0, 1}})) == 1);
assert(count_points(mapbox::util::variant<my_point>(my_point{0, 1})) == 1);
}
static void testEnvelope() {
assert(envelope(point<double>(0, 0)) == box<double>({0, 0}, {0, 0}));
assert(envelope(line_string<double>({{0, 1}, {2, 3}})) == box<double>({0, 1}, {2, 3}));
assert(envelope(polygon<double>({{{0, 1}, {2, 3}}})) == box<double>({0, 1}, {2, 3}));
assert(envelope(multi_point<double>({{0, 0}})) == box<double>({0, 0}, {0, 0}));
assert(envelope(multi_line_string<double>({{{0, 1}, {2, 3}}})) == box<double>({0, 1}, {2, 3}));
assert(envelope(multi_polygon<double>({{{{0, 1}, {2, 3}}}})) == box<double>({0, 1}, {2, 3}));
assert(envelope(geometry<int>(point<int>(0, 0))) == box<int>({0, 0}, {0, 0}));
assert(envelope(geometry_collection<int>({point<int>(0, 0)})) == box<int>({0, 0}, {0, 0}));
}
int main() {
testPoint();
testMultiPoint();
testLineString();
testMultiLineString();
testPolygon();
testMultiPolygon();
testGeometry();
testGeometryCollection();
testFeature();
testFeatureCollection();
testForEachPoint();
testEnvelope();
return 0;
}