Skip to content
GitLab
Explore
Sign in
Register
Commits on Source
6
New upstream version 2.2.1
· 58bc0549
Bas Couwenberg
authored
Dec 08, 2018
58bc0549
Merge tag 'upstream/2.2.1'
· 2b607dca
Bas Couwenberg
authored
Dec 08, 2018
Upstream version 2.2.1
2b607dca
New upstream release.
· e12070ba
Bas Couwenberg
authored
Dec 08, 2018
e12070ba
Add spatialite-bin to build dependencies for tests.
· c482fbe0
Bas Couwenberg
authored
Dec 08, 2018
c482fbe0
Also run ctest tests.
· 1ee2f37d
Bas Couwenberg
authored
Dec 08, 2018
1ee2f37d
Set distribution to unstable.
· 64ccabd2
Bas Couwenberg
authored
Dec 08, 2018
64ccabd2
Show whitespace changes
Inline
Side-by-side
.travis.yml
View file @
64ccabd2
...
...
@@ -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-gcc4
9-release
compiler
:
linux-gcc4
8-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-gcc
5
-dev
compiler
:
linux-gcc
6
-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-gcc
6-release
compiler
:
linux-gcc
7-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-gcc
6-dev
compiler
:
linux-gcc
7-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
:
xcode
8.3
compiler
:
xcode
8
-clang-release
osx_image
:
xcode
10.1
compiler
:
xcode
10
-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
...
...
CHANGELOG.md
View file @
64ccabd2
...
...
@@ -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
...
...
CMakeLists.txt
View file @
64ccabd2
...
...
@@ -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
...
...
README.md
View file @
64ccabd2
...
...
@@ -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.
...
...
cmake/FindOsmium.cmake
View file @
64ccabd2
...
...
@@ -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
}
"
)
...
...
debian/changelog
View file @
64ccabd2
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) unstable; urgency=medium
* New upstream release.
...
...
debian/control
View file @
64ccabd2
...
...
@@ -13,6 +13,7 @@ Build-Depends: debhelper (>= 9),
libgdalcpp-dev,
libspatialite-dev,
pandoc,
spatialite-bin,
sqlite3,
zlib1g-dev
Standards-Version: 4.2.1
...
...
debian/rules
View file @
64ccabd2
...
...
@@ -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
...
...
man/osmcoastline.md
View file @
64ccabd2
...
...
@@ -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 contain
s
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.
OSMC
oastline
Sometimes there is a gap in the coastline in the OSM data.
**osmc
oastline
**
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.
...
...
man/osmcoastline_filter.md
View file @
64ccabd2
...
...
@@ -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
...
...
man/osmcoastline_segments.md
View file @
64ccabd2
...
...
@@ -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.
...
...
simplify_and_split_postgis/README
View file @
64ccabd2
...
...
@@ -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:
...
...
src/CMakeLists.txt
View file @
64ccabd2
...
...
@@ -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
)
src/coastline_polygons.cpp
View file @
64ccabd2
...
...
@@ -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
()))
{
...
...
src/coastline_polygons.hpp
View file @
64ccabd2
...
...
@@ -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
),
...
...
src/coastline_ring.cpp
View file @
64ccabd2
...
...
@@ -30,25 +30,25 @@
#include
<iostream>
#include
<utility>
void
CoastlineRing
::
setup_
posi
tions
(
pos
map_type
&
pos
map
)
{
void
CoastlineRing
::
setup_
loca
tions
(
loc
map_type
&
loc
map
)
{
for
(
auto
&
wn
:
m_way_node_list
)
{
pos
map
.
insert
(
std
::
make_pair
(
wn
.
ref
(),
&
(
wn
.
location
())));
loc
map
.
insert
(
std
::
make_pair
(
wn
.
ref
(),
&
(
wn
.
location
())));
}
}
unsigned
int
CoastlineRing
::
check_
posi
tions
(
bool
output_missing
)
{
unsigned
int
missing_
posi
tions
=
0
;
unsigned
int
CoastlineRing
::
check_
loca
tions
(
bool
output_missing
)
{
unsigned
int
missing_
loca
tions
=
0
;
for
(
const
auto
&
wn
:
m_way_node_list
)
{
if
(
!
wn
.
location
())
{
++
missing_
posi
tions
;
++
missing_
loca
tions
;
if
(
output_missing
)
{
std
::
cerr
<<
"Missing
posi
tion of node "
<<
wn
.
ref
()
<<
"
\n
"
;
std
::
cerr
<<
"Missing
loca
tion of node "
<<
wn
.
ref
()
<<
"
\n
"
;
}
}
}
return
missing_
posi
tions
;
return
missing_
loca
tions
;
}
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_
posi
tion
()
!=
other
.
first_
posi
tion
())
{
if
(
last_
loca
tion
()
!=
other
.
first_
loca
tion
())
{
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_
posi
tion
()
!=
last_
posi
tion
())
{
if
(
first_
loca
tion
()
!=
last_
loca
tion
())
{
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
());
...
...
src/coastline_ring.hpp
View file @
64ccabd2
...
...
@@ -36,7 +36,7 @@ class OGRPoint;
class
OGRLineString
;
class
OGRPolygon
;
using
pos
map_type
=
std
::
multimap
<
osmium
::
object_id_type
,
osmium
::
Location
*>
;
using
loc
map_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
();
}
///
Posi
tion of the first node in the ring.
osmium
::
Location
first_
posi
tion
()
const
noexcept
{
///
Loca
tion of the first node in the ring.
osmium
::
Location
first_
loca
tion
()
const
noexcept
{
assert
(
!
m_way_node_list
.
empty
());
return
m_way_node_list
.
front
().
location
();
}
///
Posi
tion of the last node in the ring.
osmium
::
Location
last_
posi
tion
()
const
noexcept
{
///
Loca
tion of the last node in the ring.
osmium
::
Location
last_
loca
tion
()
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
posi
tion
* When there are two different nodes with the same
loca
tion
* a situation can arise where a CoastlineRing looks not closed
* when looking at the node IDs but looks closed then looking
* at the
posi
tion
s
. To "fix" this we change the node ID of the
* at the
loca
tion. 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
posi
tions to the given
pos
map. The
*
pos
map can than later be used to directly put the
posi
tions
* Add pointers to the node
loca
tions to the given
loc
map. The
*
loc
map can than later be used to directly put the
loca
tions
* into the right place.
*/
void
setup_
posi
tions
(
pos
map_type
&
pos
map
);
void
setup_
loca
tions
(
loc
map_type
&
loc
map
);
/**
* Check whether all
posi
tions for the ways are there. This
* Check whether all
node loca
tions 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
posi
tions.
* way. The function returns the number of missing
loca
tions.
*/
unsigned
int
check_
posi
tions
(
bool
output_missing
);
unsigned
int
check_
loca
tions
(
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_
posi
tion
(
osmium
::
Location
pos
)
const
;
double
distance_to_start_
loca
tion
(
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_
posi
tion
()
<
rhs
.
first_
posi
tion
();
return
lhs
.
first_
loca
tion
()
<
rhs
.
first_
loca
tion
();
}
#endif // COASTLINE_RING_HPP
src/coastline_ring_collection.cpp
View file @
64ccabd2
...
...
@@ -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_
posi
tions
(
pos
map_type
&
pos
map
)
{
void
CoastlineRingCollection
::
setup_
loca
tions
(
loc
map_type
&
loc
map
)
{
for
(
const
auto
&
ring
:
m_list
)
{
ring
->
setup_
posi
tions
(
pos
map
);
ring
->
setup_
loca
tions
(
loc
map
);
}
}
unsigned
int
CoastlineRingCollection
::
check_
posi
tions
(
bool
output_missing
)
{
unsigned
int
missing_
posi
tions
=
0
;
unsigned
int
CoastlineRingCollection
::
check_
loca
tions
(
bool
output_missing
)
{
unsigned
int
missing_
loca
tions
=
0
;
for
(
const
auto
&
ring
:
m_list
)
{
missing_
posi
tions
+=
ring
->
check_
posi
tions
(
output_missing
);
missing_
loca
tions
+=
ring
->
check_
loca
tions
(
output_missing
);
}
return
missing_
posi
tions
;
return
missing_
loca
tions
;
}
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_
posi
tion
();
const
osmium
::
Location
lpos
=
ring
->
last_
posi
tion
();
const
osmium
::
Location
fpos
=
ring
->
first_
loca
tion
();
const
osmium
::
Location
lpos
=
ring
->
last_
loca
tion
();
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_
posi
tion
((
*
end_node
.
second
)
->
last_
posi
tion
());
const
double
distance
=
(
*
start_node
.
second
)
->
distance_to_start_
loca
tion
((
*
end_node
.
second
)
->
last_
loca
tion
());
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_
posi
tion
()
!=
s
->
first_
posi
tion
())
{
if
(
e
->
last_
loca
tion
()
!=
s
->
first_
loca
tion
())
{
std
::
unique_ptr
<
OGRLineString
>
linestring
{
new
OGRLineString
};
linestring
->
addPoint
(
e
->
last_
posi
tion
().
lon
(),
e
->
last_
posi
tion
().
lat
());
linestring
->
addPoint
(
s
->
first_
posi
tion
().
lon
(),
s
->
first_
posi
tion
().
lat
());
linestring
->
addPoint
(
e
->
last_
loca
tion
().
lon
(),
e
->
last_
loca
tion
().
lat
());
linestring
->
addPoint
(
s
->
first_
loca
tion
().
lon
(),
s
->
first_
loca
tion
().
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_
posi
tion
()
==
e
->
last_
posi
tion
())
{
if
(
e
->
first_
loca
tion
()
==
e
->
last_
loca
tion
())
{
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_
posi
tion
(),
ring
.
get
());
rings
.
emplace_back
(
ring
->
first_
loca
tion
(),
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
posi
tion of the first node in the ring (this allows binary search in it)
// ... and sort it by
loca
tion 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
())
{
...
...
src/coastline_ring_collection.hpp
View file @
64ccabd2
...
...
@@ -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_
posi
tions
(
pos
map_type
&
pos
map
);
void
setup_
loca
tions
(
loc
map_type
&
loc
map
);
unsigned
int
check_
posi
tions
(
bool
output_missing
);
unsigned
int
check_
loca
tions
(
bool
output_missing
);
std
::
vector
<
OGRGeometry
*>
add_polygons_to_vector
();
...
...
src/nodegrid2opl.cpp
0 → 100644
View file @
64ccabd2
/*
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
;
}
Prev
1
2
3
Next