Skip to content
Commits on Source (8)
......@@ -6,186 +6,120 @@
language: generic
sudo: false
dist: trusty
dist: xenial
#-----------------------------------------------------------------------------
matrix:
include:
# 1/ Linux Clang Builds
- os: linux
compiler: linux-clang38-release
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['clang-3.8', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
env: CXX='clang++-3.8' BUILD_TYPE='Release'
# Linux Clang Builds
- os: linux
compiler: linux-clang38-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['clang-3.8', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
packages: ['clang-3.8', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'spatialite-bin']
env: CXX='clang++-3.8' BUILD_TYPE='Dev'
- os: linux
compiler: linux-clang39-release
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['clang-3.9', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
env: CXX='clang++-3.9' BUILD_TYPE='Release'
- os: linux
compiler: linux-clang39-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['clang-3.9', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
packages: ['clang-3.9', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'spatialite-bin']
env: CXX='clang++-3.9' BUILD_TYPE='Dev'
- os: linux
compiler: linux-clang40-release
addons:
apt:
sources: ['llvm-toolchain-trusty-4.0', 'ubuntu-toolchain-r-test']
packages: ['clang-4.0', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
env: CXX='clang++-4.0' BUILD_TYPE='Release'
- os: linux
compiler: linux-clang40-dev
addons:
apt:
sources: ['llvm-toolchain-trusty-4.0', 'ubuntu-toolchain-r-test']
packages: ['clang-4.0', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
packages: ['clang-4.0', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'spatialite-bin']
env: CXX='clang++-4.0' BUILD_TYPE='Dev'
- os: linux
compiler: linux-clang50-release
addons:
apt:
sources: ['llvm-toolchain-trusty-5.0', 'ubuntu-toolchain-r-test']
packages: ['clang-5.0', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
env: CXX='clang++-5.0' BUILD_TYPE='Release'
- os: linux
compiler: linux-clang50-dev
addons:
apt:
sources: ['llvm-toolchain-trusty-5.0', 'ubuntu-toolchain-r-test']
packages: ['clang-5.0', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'clang-tidy-5.0']
env: CXX='clang++-5.0' BUILD_TYPE='Dev' CLANG_TIDY=clang-tidy-5.0
packages: ['clang-5.0', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'spatialite-bin']
env: CXX='clang++-5.0' BUILD_TYPE='Dev'
# 2/ Linux GCC Builds
- os: linux
compiler: linux-gcc48-release
compiler: linux-clang60-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.8', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
env: CXX='g++-4.8' CXXFLAGS='-Wno-return-type' BUILD_TYPE='Release'
packages: ['clang-6.0', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'spatialite-bin', 'clang-tidy-6.0']
env: CXX='clang++-6.0' BUILD_TYPE='Dev' CLANG_TIDY=clang-tidy-6.0
- os: linux
compiler: linux-gcc48-dev
compiler: linux-clang60-release
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.8', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
env: CXX='g++-4.8' CXXFLAGS='-Wno-return-type' BUILD_TYPE='Dev'
packages: ['clang-6.0', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'spatialite-bin']
env: CXX='clang++-6.0' BUILD_TYPE='Release'
# Linux GCC Builds
- os: linux
compiler: linux-gcc49-release
compiler: linux-gcc48-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.9', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
env: CXX='g++-4.9' BUILD_TYPE='Release'
packages: ['g++-4.8', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'spatialite-bin']
env: CXX='g++-4.8' BUILD_TYPE='Dev' CXXFLAGS='-Wno-return-type'
- os: linux
compiler: linux-gcc49-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.9', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
packages: ['g++-4.9', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'spatialite-bin']
env: CXX='g++-4.9' BUILD_TYPE='Dev'
- os: linux
compiler: linux-gcc5-release
compiler: linux-gcc5-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-5', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
env: CXX='g++-5' BUILD_TYPE='Release'
packages: ['g++-5', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'spatialite-bin']
env: CXX='g++-5' BUILD_TYPE='Dev'
- os: linux
compiler: linux-gcc5-dev
compiler: linux-gcc6-dev
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: ['g++-5', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
env: CXX='g++-5' BUILD_TYPE='Dev'
packages: ['g++-6', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'spatialite-bin']
env: CXX='g++-6' BUILD_TYPE='Dev'
- os: linux
compiler: linux-gcc6-release
compiler: linux-gcc7-dev
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: ['g++-6', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
env: CXX='g++-6' BUILD_TYPE='Release'
packages: ['g++-7', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'spatialite-bin']
env: CXX='g++-7' BUILD_TYPE='Dev'
- os: linux
compiler: linux-gcc6-dev
compiler: linux-gcc7-release
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: ['g++-6', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3']
env: CXX='g++-6' BUILD_TYPE='Dev'
packages: ['g++-7', 'pandoc', 'libgdal1-dev', 'libgeos-dev', 'sqlite3', 'spatialite-bin']
env: CXX='g++-7' BUILD_TYPE='Release'
# 3/ OSX Clang Builds
# OSX Clang Builds
- os: osx
osx_image: xcode6.4
compiler: xcode64-clang-release
env: CXX='clang++' BUILD_TYPE='Release'
- os: osx
osx_image: xcode6.4
compiler: xcode64-clang-dev
osx_image: xcode9.4
compiler: xcode9-clang-dev
env: CXX='clang++' BUILD_TYPE='Dev'
- os: osx
osx_image: xcode7
compiler: xcode7-clang-release
env: CXX='clang++' BUILD_TYPE='Release'
- os: osx
osx_image: xcode7
compiler: xcode7-clang-dev
osx_image: xcode10.1
compiler: xcode10-clang-dev
env: CXX='clang++' BUILD_TYPE='Dev'
- os: osx
osx_image: xcode8.3
compiler: xcode8-clang-release
osx_image: xcode10.1
compiler: xcode10-clang-release
env: CXX='clang++' BUILD_TYPE='Release'
- os: osx
osx_image: xcode8.3
compiler: xcode8-clang-dev
env: CXX='clang++' BUILD_TYPE='Dev'
#-----------------------------------------------------------------------------
install:
......@@ -194,6 +128,7 @@ install:
- |
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
brew install cmake || true
brew install spatialite-tools || true
fi
- cmake --version
......
......@@ -13,6 +13,22 @@ This project adheres to [Semantic Versioning](https://semver.org/).
### Fixed
## [2.2.1] - 2018-12-07
### Added
- We have now proper test cases. Just a few, but at least there is a framework
for automated testing now.
### Changed
- Various small changes in the code and manuals to make it clearer.
### Fixed
- Various small bugs were fixed that lead to crashes in unusual circumstances.
## [2.2.0] - 2018-09-05
### Added
......
......@@ -20,7 +20,7 @@ project(osmcoastline)
set(OSMCOASTLINE_VERSION_MAJOR 2)
set(OSMCOASTLINE_VERSION_MINOR 2)
set(OSMCOASTLINE_VERSION_PATCH 0)
set(OSMCOASTLINE_VERSION_PATCH 1)
set(OSMCOASTLINE_VERSION
${OSMCOASTLINE_VERSION_MAJOR}.${OSMCOASTLINE_VERSION_MINOR}.${OSMCOASTLINE_VERSION_PATCH})
......@@ -323,6 +323,16 @@ install(PROGRAMS osmcoastline_readmeta DESTINATION bin)
add_subdirectory(doc)
#-----------------------------------------------------------------------------
#
# Tests
#
#-----------------------------------------------------------------------------
enable_testing()
add_subdirectory(test)
#-----------------------------------------------------------------------------
#
# Packaging
......
......@@ -46,7 +46,7 @@ https://github.com/osmcode/osmcoastline
### Sqlite/Spatialite
https://www.gaia-gis.it/fossil/libspatialite/index
Debian/Ubuntu: sqlite3
Debian/Ubuntu: sqlite3, spatialite-bin
### Pandoc (optional, to build documentation)
......@@ -72,6 +72,15 @@ in the `doc/html` directory.
## Testing
A few tests are provided that can be run by calling `ctest`.
The tests themselves are written as shell scripts and can be found in the
`test/t` directory. Some test use the `nodegrid2opl` helper program found in
the `src` directory, it has some documentation in the source code.
## The `runtest` script
Run the script `runtest.sh` from the directory you built the program in. It
will read the supplied `testdata.osm` and create output in the `testdata.db`
spatialite database.
......
......@@ -71,6 +71,9 @@ find_path(OSMIUM_INCLUDE_DIR osmium/version.hpp
# Check libosmium version number
if(Osmium_FIND_VERSION)
if(NOT EXISTS "${OSMIUM_INCLUDE_DIR}/osmium/version.hpp")
message(FATAL_ERROR "Missing ${OSMIUM_INCLUDE_DIR}/osmium/version.hpp. Either your libosmium version is too old, or libosmium wasn't found in the place you said.")
endif()
file(STRINGS "${OSMIUM_INCLUDE_DIR}/osmium/version.hpp" _libosmium_version_define REGEX "#define LIBOSMIUM_VERSION_STRING")
if("${_libosmium_version_define}" MATCHES "#define LIBOSMIUM_VERSION_STRING \"([0-9.]+)\"")
set(_libosmium_version "${CMAKE_MATCH_1}")
......
osmcoastline (2.2.1-1~bpo9+1) stretch-backports; urgency=medium
* Rebuild for stretch-backports.
-- Bas Couwenberg <sebastic@debian.org> Mon, 17 Dec 2018 10:18:42 +0100
osmcoastline (2.2.1-1) unstable; urgency=medium
* New upstream release.
* Add spatialite-bin to build dependencies for tests.
* Also run ctest tests.
-- Bas Couwenberg <sebastic@debian.org> Sat, 08 Dec 2018 10:16:15 +0100
osmcoastline (2.2.0-1~bpo9+1) stretch-backports; urgency=medium
* Rebuild for stretch-backports.
......
......@@ -13,6 +13,7 @@ Build-Depends: debhelper (>= 9),
libgdalcpp-dev,
libspatialite-dev,
pandoc,
spatialite-bin,
sqlite3,
zlib1g-dev
Standards-Version: 4.2.1
......
......@@ -13,6 +13,8 @@ override_dh_auto_configure:
dh_auto_configure -- -DCMAKE_VERBOSE_MAKEFILE=1
override_dh_auto_test:
dh_auto_test
ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
(cd obj-* && ./runtest.sh || echo "Ignoring expected test failure")
endif
......
......@@ -6,7 +6,7 @@ osmcoastline - extract coastline from OpenStreetMap data
# SYNOPSIS
**osmcoastline** \[*OPTIONS*\] --output=*OUTPUT-DB* *INPUT-FILE*
**osmcoastline** \[*OPTIONS*\] --output-database=*OUTPUT-DB* *INPUT-FILE*
# DESCRIPTION
......@@ -16,7 +16,7 @@ a planet file (or the output of the **osmcoastline_filter** program, see below)
and assembles all the pieces into polygons for use in map renderers etc.
The output is written to the Spatialite database *OUTPUT-DB*. Depending on the
options it will contains the coastlines in different formats. See the
options it will contain the coastlines in different formats. See the
description of the options below and the README.md for details.
......@@ -29,16 +29,17 @@ description of the options below and the README.md for details.
: Polygons that are too large are split into two halves (recursively if need
be). Where the polygons touch the OVERLAP is added, because two polygons
just touching often lead to rendering problems. The value is given in the
units used for the projection (for WGS84 (4326) this is in degrees, for
Mercator (3857) this is in meters). If this is set too small you might get
rendering artefacts where polygons touch. The larger you set this the
larger the output polygons will be. The best values depend on the map scale
or zoom level you are preparing the data for. Disable the overlap by
setting it to 0. Default is 0.0001 for WGS84 and 10 for Mercator.
units used for the projection (for WGS84 (EPSG: 4326) this is in degrees,
for Mercator (EPSG: 3857) this is in meters). If this is set too small you
might get rendering artefacts where polygons touch. The larger you set
this the larger the output polygons will be. The best values depend on
the map scale or zoom level you are preparing the data for. Disable the
overlap by setting it to 0. Default is 0.0001 for WGS84 and 10 for
Mercator.
-c, --close-distance=DISTANCE
: **osmcoastline** assembles ways tagged `natural=coastline` into rings.
Sometimes there is a gap in the coastline in the OSM data. OSMCoastline
Sometimes there is a gap in the coastline in the OSM data. **osmcoastline**
will close this gap if it is smaller than DISTANCE. Use 0 to disable this
feature.
......@@ -50,7 +51,7 @@ description of the options below and the README.md for details.
-i, --no-index
: Do not create spatial indexes in output db. The default is to create those
indexes which makes the database larger, but the data is faster to use.
indexes. This makes the database larger, but data access is faster.
-l, --output-lines
: Output coastlines as lines to database file.
......@@ -63,14 +64,14 @@ description of the options below and the README.md for details.
sometimes not possible to get the polygons small enough. **osmcoastline**
will warn you on STDERR if this is the case. Default is 1000.
-o, --output=FILE
-o, --output-database=FILE
: Spatialite database file for output. This option must be set.
-p, --output-polygons=land|water|both|none
: Which polygons to write out (default: land).
-r, --output-rings
: Output rings to database file. This is used for debugging purposes.
: Output rings to database file. This is used for debugging.
-s, --srs=EPSGCODE
: Set spatial reference system/projection. Use 4326 for WGS84 or 3857 for
......@@ -107,12 +108,16 @@ program first. See its man page for details.
0
~ if everything was okay
1
~ if there were warnings while processing the coastline
2
~ if there were errors while processing the coastline
3
~ if there was a fatal error when running the program
4
~ if there was a problem with the command line arguments.
......
......@@ -25,7 +25,8 @@ If you are playing around or want to run **osmcoastline** several times with
different parameters, run **osmcoastline_filter** once first and use its output
as the input for **osmcoastline**.
**osmcoastline_filter** can read PBF and XML files, but write only PBF files.
**osmcoastline_filter** can read any file format supported by libosmium (in
particular this is PBF, XML, and OPL files), but write only PBF files.
PBF files are much smaller and faster to read and write than XML files. The
output file will first contain all ways tagged "natural=coastline", then all
nodes used for those ways (and all nodes tagged "natural=coastline"). Having
......
......@@ -41,10 +41,13 @@ coastline changes between different runs of the **osmcoastline** program.
0
~ if the segment files are the same
1
~ if the segment files are different
3
~ if there was a fatal error when running the program
4
~ if there was a problem with the command line arguments.
......
......@@ -46,9 +46,9 @@ tolerances, etc.:
5. For every zoom level you want, you have to prepare tile tables:
# psql -d coastlines -v zoom=3 setup_bbox_tiles.sql
# psql -d coastlines -v zoom=5 setup_bbox_tiles.sql
# psql -d coastlines -v zoom=6 setup_bbox_tiles.sql
# psql -d coastlines -v zoom=3 -f setup_bbox_tiles.sql
# psql -d coastlines -v zoom=5 -f setup_bbox_tiles.sql
# psql -d coastlines -v zoom=6 -f setup_bbox_tiles.sql
6. For every simplification step you need, do the simplification:
......
......@@ -34,3 +34,6 @@ target_link_libraries(osmcoastline_ways ${OSMIUM_IO_LIBRARIES} ${GDAL_LIBRARIES}
set_pthread_on_target(osmcoastline_ways)
install(TARGETS osmcoastline_ways DESTINATION bin)
# only used for testing - should not be installed
add_executable(nodegrid2opl nodegrid2opl.cpp)
......@@ -65,14 +65,17 @@ unsigned int CoastlinePolygons::fix_direction() {
for (const auto& polygon : m_polygons) {
OGRLinearRing* er = polygon->getExteriorRing();
assert(er);
if (!er->isClockwise()) {
er->reverseWindingOrder();
// Workaround for bug in OGR: reverseWindingOrder sets dimensions to 3
er->setCoordinateDimension(2);
for (int i = 0; i < polygon->getNumInteriorRings(); ++i) {
polygon->getInteriorRing(i)->reverseWindingOrder();
OGRLinearRing* ir = polygon->getInteriorRing(i);
assert(ir);
ir->reverseWindingOrder();
// Workaround for bug in OGR: reverseWindingOrder sets dimensions to 3
polygon->getInteriorRing(i)->setCoordinateDimension(2);
ir->setCoordinateDimension(2);
}
m_output.add_error_line(make_unique_ptr_clone<OGRLineString>(er), "direction");
warnings++;
......@@ -92,7 +95,7 @@ void CoastlinePolygons::split_geometry(std::unique_ptr<OGRGeometry>&& geom, int
if (geom->getGeometryType() == wkbPolygon) {
geom->assignSpatialReference(srs.out());
split_polygon(static_cast_unique_ptr<OGRPolygon>(std::move(geom)), level);
} else { // wkbMultiPolygon
} else if (geom->getGeometryType() == wkbMultiPolygon) {
const auto mp = static_cast_unique_ptr<OGRMultiPolygon>(std::move(geom));
while (mp->getNumGeometries() > 0) {
std::unique_ptr<OGRPolygon> polygon{static_cast<OGRPolygon*>(mp->getGeometryRef(0))};
......@@ -100,6 +103,8 @@ void CoastlinePolygons::split_geometry(std::unique_ptr<OGRGeometry>&& geom, int
polygon->assignSpatialReference(srs.out());
split_polygon(std::move(polygon), level);
}
} else {
assert(false);
}
}
......@@ -247,6 +252,7 @@ void CoastlinePolygons::add_line_to_output(std::unique_ptr<OGRLineString> line,
// Add a coastline ring as LineString to output. Segments in this line that are
// near the southern edge of the map or near the antimeridian are suppressed.
void CoastlinePolygons::output_polygon_ring_as_lines(int max_points, const OGRLinearRing* ring) const {
assert(ring);
const int num = ring->getNumPoints();
assert(num > 2);
......@@ -307,6 +313,7 @@ void CoastlinePolygons::split_bbox(OGREnvelope e, polygon_vector_type&& v) {
assert(geom->getSpatialReference() != nullptr);
for (const auto& polygon : v) {
std::unique_ptr<OGRGeometry> diff{geom->Difference(polygon.get())};
assert(diff);
// for some reason there is sometimes no srs on the geometries, so we add them on
diff->assignSpatialReference(srs.out());
geom = std::move(diff);
......@@ -322,6 +329,7 @@ void CoastlinePolygons::split_bbox(OGREnvelope e, polygon_vector_type&& v) {
auto mp = static_cast_unique_ptr<OGRMultiPolygon>(std::move(geom));
for (int i = mp->getNumGeometries() - 1; i >= 0; --i) {
auto p = std::unique_ptr<OGRPolygon>(static_cast<OGRPolygon*>(mp->getGeometryRef(i)));
assert(p);
mp->removeGeometry(i, FALSE);
p->assignSpatialReference(mp->getSpatialReference());
if (!antarctica_bogus(p.get())) {
......
......@@ -81,7 +81,7 @@ class CoastlinePolygons {
public:
CoastlinePolygons(polygon_vector_type&& polygons, OutputDatabase& output, double expand, unsigned int max_points_in_polygon) :
CoastlinePolygons(polygon_vector_type&& polygons, OutputDatabase& output, double expand, int max_points_in_polygon) :
m_output(output),
m_expand(expand),
m_max_points_in_polygon(max_points_in_polygon),
......
......@@ -30,25 +30,25 @@
#include <iostream>
#include <utility>
void CoastlineRing::setup_positions(posmap_type& posmap) {
void CoastlineRing::setup_locations(locmap_type& locmap) {
for (auto& wn : m_way_node_list) {
posmap.insert(std::make_pair(wn.ref(), &(wn.location())));
locmap.insert(std::make_pair(wn.ref(), &(wn.location())));
}
}
unsigned int CoastlineRing::check_positions(bool output_missing) {
unsigned int missing_positions = 0;
unsigned int CoastlineRing::check_locations(bool output_missing) {
unsigned int missing_locations = 0;
for (const auto& wn : m_way_node_list) {
if (!wn.location()) {
++missing_positions;
++missing_locations;
if (output_missing) {
std::cerr << "Missing position of node " << wn.ref() << "\n";
std::cerr << "Missing location of node " << wn.ref() << "\n";
}
}
}
return missing_positions;
return missing_locations;
}
void CoastlineRing::add_at_front(const osmium::Way& way) {
......@@ -76,7 +76,7 @@ void CoastlineRing::join(const CoastlineRing& other) {
}
void CoastlineRing::join_over_gap(const CoastlineRing& other) {
if (last_position() != other.first_position()) {
if (last_location() != other.first_location()) {
m_way_node_list.push_back(other.m_way_node_list.front());
}
......@@ -88,7 +88,7 @@ void CoastlineRing::join_over_gap(const CoastlineRing& other) {
}
void CoastlineRing::close_ring() {
if (first_position() != last_position()) {
if (first_location() != last_location()) {
m_way_node_list.push_back(m_way_node_list.front());
}
m_fixed = true;
......@@ -140,18 +140,21 @@ std::unique_ptr<OGRLineString> CoastlineRing::ogr_linestring(osmium::geom::OGRFa
}
std::unique_ptr<OGRPoint> CoastlineRing::ogr_first_point() const {
assert(!m_way_node_list.empty());
const osmium::NodeRef& node_ref = m_way_node_list.front();
return std::unique_ptr<OGRPoint>{new OGRPoint{node_ref.lon(), node_ref.lat()}};
}
std::unique_ptr<OGRPoint> CoastlineRing::ogr_last_point() const {
assert(!m_way_node_list.empty());
const osmium::NodeRef& node_ref = m_way_node_list.back();
return std::unique_ptr<OGRPoint>{new OGRPoint{node_ref.lon(), node_ref.lat()}};
}
// Pythagoras doesn't work on a round earth but that is ok here, we only need a
// rough measure anyway
double CoastlineRing::distance_to_start_position(osmium::Location pos) const {
double CoastlineRing::distance_to_start_location(osmium::Location pos) const {
assert(!m_way_node_list.empty());
const osmium::Location p = m_way_node_list.front().location();
return (pos.lon() - p.lon()) * (pos.lon() - p.lon()) +
(pos.lat() - p.lat()) * (pos.lat() - p.lat());
......
......@@ -36,7 +36,7 @@ class OGRPoint;
class OGRLineString;
class OGRPolygon;
using posmap_type = std::multimap<osmium::object_id_type, osmium::Location*>;
using locmap_type = std::multimap<osmium::object_id_type, osmium::Location*>;
/**
* The CoastlineRing class models a (possibly unfinished) ring of
......@@ -91,6 +91,7 @@ public:
*/
explicit CoastlineRing(const osmium::Way& way) :
m_ring_id(way.id()) {
assert(!way.nodes().empty());
m_way_node_list.reserve(way.is_closed() ? way.nodes().size() : 1000);
m_way_node_list.insert(m_way_node_list.begin(), way.nodes().begin(), way.nodes().end());
}
......@@ -115,14 +116,14 @@ public:
return m_way_node_list.back().ref();
}
/// Position of the first node in the ring.
osmium::Location first_position() const noexcept {
/// Location of the first node in the ring.
osmium::Location first_location() const noexcept {
assert(!m_way_node_list.empty());
return m_way_node_list.front().location();
}
/// Position of the last node in the ring.
osmium::Location last_position() const noexcept {
/// Location of the last node in the ring.
osmium::Location last_location() const noexcept {
assert(!m_way_node_list.empty());
return m_way_node_list.back().location();
}
......@@ -163,10 +164,10 @@ public:
}
/**
* When there are two different nodes with the same position
* When there are two different nodes with the same location
* a situation can arise where a CoastlineRing looks not closed
* when looking at the node IDs but looks closed then looking
* at the positions. To "fix" this we change the node ID of the
* at the location. To "fix" this we change the node ID of the
* last node in the ring to be the same as the first. This
* method does this.
*/
......@@ -176,18 +177,18 @@ public:
}
/**
* Add pointers to the node positions to the given posmap. The
* posmap can than later be used to directly put the positions
* Add pointers to the node locations to the given locmap. The
* locmap can than later be used to directly put the locations
* into the right place.
*/
void setup_positions(posmap_type& posmap);
void setup_locations(locmap_type& locmap);
/**
* Check whether all positions for the ways are there. This
* Check whether all node locations for the ways are there. This
* can happen if the input data is missing a node needed for a
* way. The function returns the number of missing positions.
* way. The function returns the number of missing locations.
*/
unsigned int check_positions(bool output_missing);
unsigned int check_locations(bool output_missing);
/// Add a new way to the front of this ring.
void add_at_front(const osmium::Way& way);
......@@ -254,7 +255,7 @@ public:
*/
std::unique_ptr<OGRPoint> ogr_last_point() const;
double distance_to_start_position(osmium::Location pos) const;
double distance_to_start_location(osmium::Location pos) const;
void add_segments_to_vector(std::vector<osmium::UndirectedSegment>& segments) const;
......@@ -263,7 +264,7 @@ public:
}; // class CoastlineRing
inline bool operator<(const CoastlineRing& lhs, const CoastlineRing& rhs) noexcept {
return lhs.first_position() < rhs.first_position();
return lhs.first_location() < rhs.first_location();
}
#endif // COASTLINE_RING_HPP
......@@ -32,7 +32,6 @@
#ifdef _MSC_VER
#include <io.h>
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif
extern SRS srs;
......@@ -46,6 +45,7 @@ extern bool debug;
* CoastlineRing for it and add that to the collection.
*/
void CoastlineRingCollection::add_partial_ring(const osmium::Way& way) {
assert(!way.nodes().empty());
const auto mprev = m_end_nodes.find(way.nodes().front().ref());
const auto mnext = m_start_nodes.find(way.nodes().back().ref());
......@@ -66,7 +66,10 @@ void CoastlineRingCollection::add_partial_ring(const osmium::Way& way) {
m_end_nodes.erase(mprev);
if ((*prev)->is_closed()) {
m_start_nodes.erase(m_start_nodes.find((*prev)->first_node_id()));
const auto found = m_start_nodes.find((*prev)->first_node_id());
if (found != m_start_nodes.end()) {
m_start_nodes.erase(found);
}
return;
}
......@@ -100,27 +103,30 @@ void CoastlineRingCollection::add_partial_ring(const osmium::Way& way) {
(*next)->add_at_front(way);
m_start_nodes.erase(mnext);
if ((*next)->is_closed()) {
m_end_nodes.erase(m_end_nodes.find((*next)->last_node_id()));
const auto found = m_end_nodes.find((*next)->last_node_id());
if (found != m_end_nodes.end()) {
m_end_nodes.erase(found);
}
return;
}
m_start_nodes[(*next)->first_node_id()] = next;
}
}
void CoastlineRingCollection::setup_positions(posmap_type& posmap) {
void CoastlineRingCollection::setup_locations(locmap_type& locmap) {
for (const auto& ring : m_list) {
ring->setup_positions(posmap);
ring->setup_locations(locmap);
}
}
unsigned int CoastlineRingCollection::check_positions(bool output_missing) {
unsigned int missing_positions = 0;
unsigned int CoastlineRingCollection::check_locations(bool output_missing) {
unsigned int missing_locations = 0;
for (const auto& ring : m_list) {
missing_positions += ring->check_positions(output_missing);
missing_locations += ring->check_locations(output_missing);
}
return missing_positions;
return missing_locations;
}
bool is_valid_polygon(const OGRGeometry* geometry) {
......@@ -263,16 +269,21 @@ unsigned int CoastlineRingCollection::check_for_intersections(OutputDatabase& ou
std::cerr << "Writing segments to file...\n";
}
const ssize_t length = segments.size() * sizeof(osmium::UndirectedSegment);
const auto length = segments.size() * sizeof(osmium::UndirectedSegment);
#ifndef _MSC_VER
if (::write(segments_fd, segments.data(), length) != length) {
if (::write(segments_fd, segments.data(), length) != static_cast<ssize_t>(length)) {
#else
if (_write(segments_fd, segments.data(), length) != length) {
if (_write(segments_fd, segments.data(), length) != static_cast<int>(length)) {
#endif
throw std::runtime_error{"Write error"};
}
}
// There can be no intersections if there are less than two segments
if (segments.size() < 2) {
return 0;
}
if (debug) {
std::cerr << "Finding intersections...\n";
}
......@@ -310,8 +321,8 @@ unsigned int CoastlineRingCollection::check_for_intersections(OutputDatabase& ou
bool CoastlineRingCollection::close_antarctica_ring(int epsg) {
for (const auto& ring : m_list) {
const osmium::Location fpos = ring->first_position();
const osmium::Location lpos = ring->last_position();
const osmium::Location fpos = ring->first_location();
const osmium::Location lpos = ring->last_location();
if (fpos.lon() > 179.99 && lpos.lon() < -179.99 &&
fpos.lat() < -77.0 && fpos.lat() > -78.0 &&
lpos.lat() < -77.0 && lpos.lat() > -78.0) {
......@@ -331,7 +342,7 @@ void CoastlineRingCollection::close_rings(OutputDatabase& output, bool debug, do
// Create vector with all possible combinations of connections between rings.
for (const auto& end_node : m_end_nodes) {
for (const auto& start_node : m_start_nodes) {
const double distance = (*start_node.second)->distance_to_start_position((*end_node.second)->last_position());
const double distance = (*start_node.second)->distance_to_start_location((*end_node.second)->last_location());
if (distance < max_distance) {
connections.emplace_back(distance, end_node.first, start_node.first);
}
......@@ -366,10 +377,10 @@ void CoastlineRingCollection::close_rings(OutputDatabase& output, bool debug, do
output.add_error_point(e->ogr_last_point(), "fixed_end_point", e->last_node_id());
output.add_error_point(s->ogr_first_point(), "fixed_end_point", s->first_node_id());
if (e->last_position() != s->first_position()) {
if (e->last_location() != s->first_location()) {
std::unique_ptr<OGRLineString> linestring{new OGRLineString};
linestring->addPoint(e->last_position().lon(), e->last_position().lat());
linestring->addPoint(s->first_position().lon(), s->first_position().lat());
linestring->addPoint(e->last_location().lon(), e->last_location().lat());
linestring->addPoint(s->first_location().lon(), s->first_location().lat());
output.add_error_line(std::move(linestring), "added_line");
}
......@@ -384,7 +395,7 @@ void CoastlineRingCollection::close_rings(OutputDatabase& output, bool debug, do
e->join_over_gap(*s);
m_list.erase(sit->second);
if (e->first_position() == e->last_position()) {
if (e->first_location() == e->last_location()) {
output.add_error_point(e->ogr_first_point(), "double_node", e->first_node_id());
m_start_nodes.erase(e->first_node_id());
m_end_nodes.erase(eit);
......@@ -421,7 +432,7 @@ unsigned int CoastlineRingCollection::output_questionable(const CoastlinePolygon
// put all rings in a vector...
for (const auto& ring : m_list) {
rings.emplace_back(ring->first_position(), ring.get());
rings.emplace_back(ring->first_location(), ring.get());
}
// comparison function that ignores the second part of the pair
......@@ -429,12 +440,13 @@ unsigned int CoastlineRingCollection::output_questionable(const CoastlinePolygon
return a.first < b.first;
};
// ... and sort it by position of the first node in the ring (this allows binary search in it)
// ... and sort it by location of the first node in the ring (this allows binary search in it)
std::sort(rings.begin(), rings.end(), comp);
// go through all the polygons that have been created before and mark the outer rings
for (const auto& polygon : polygons) {
const OGRLinearRing* exterior_ring = polygon->getExteriorRing();
assert(exterior_ring);
osmium::Location pos{exterior_ring->getX(0), exterior_ring->getY(0)};
const auto rings_it = lower_bound(rings.begin(), rings.end(), lcrp_type{pos, nullptr}, comp);
if (rings_it != rings.end()) {
......
......@@ -77,6 +77,7 @@ public:
* or it will be joined to an existing CoastlineRing.
*/
void add_way(const osmium::Way& way) {
assert(!way.nodes().empty());
m_ways++;
if (way.is_closed()) {
m_rings_from_single_way++;
......@@ -102,9 +103,9 @@ public:
return m_fixed_rings;
}
void setup_positions(posmap_type& posmap);
void setup_locations(locmap_type& locmap);
unsigned int check_positions(bool output_missing);
unsigned int check_locations(bool output_missing);
std::vector<OGRGeometry*> add_polygons_to_vector();
......
/*
Copyright 2012-2018 Jochen Topf <jochen@topf.org>.
This file is part of OSMCoastline.
OSMCoastline 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 3 of the License, or
(at your option) any later version.
OSMCoastline 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 OSMCoastline. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* This is a program used for testing only. It interprets an "ASCII art"
* visualization of nodes in a grid read from STDIN and creates an OPL
* output on STDOUT with those nodes.
*
* Nodes are represented by the characters 0 to 9 and a to z. All other
* characters are ignored and are only used to form the grid. Here is
* an example:
*
* ------------------
*
* 0 3 1
* \
* 2--a-5
*
* ------------------
*
* This will result in something like:
*
* n100 v1 x1.030000 y1.990000
* n101 v1 x1.120000 y1.990000
* n102 v1 x1.050000 y1.970000
* n103 v1 x1.070000 y1.990000
* n105 v1 x1.100000 y1.970000
* n110 v1 x1.080000 y1.970000
*
*/
#include <algorithm>
#include <iostream>
#include <iterator>
#include <set>
#include <string>
#include <vector>
std::vector<std::string> nodes;
const int id_offset = 100;
static void add_node(char c, double x, double y) {
static std::set<int> ids;
int id = id_offset;
if (c >= '0' && c <= '9') {
id += c - '0';
} else {
id += c - 'a' + 10;
}
if (ids.count(id)) {
std::cerr << "ID seen twice: " << c << " (" << id << ")\n";
std::exit(1);
}
ids.insert(id);
nodes.push_back("n" + std::to_string(id) + " v1 x" + std::to_string(x) + " y" + std::to_string(y) + "\n");
}
int main() {
const double scale = 0.01;
const double offset = 1;
int x = 1;
int y = 100;
for (std::string line; std::getline(std::cin, line);) {
for (const auto c : line) {
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')) {
add_node(c, offset + x * scale, offset + y * scale);
}
++x;
}
x = 1;
--y;
}
std::sort(nodes.begin(), nodes.end());
const auto it = std::ostream_iterator<std::string>(std::cout);
std::copy(nodes.begin(), nodes.end(), it);
return 0;
}