Skip to content

Commit 13b841d

Browse files
authored
Visvalingam-Whyatt simplification (#772)
1 parent 868d514 commit 13b841d

File tree

10 files changed

+309
-11
lines changed

10 files changed

+309
-11
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ file(GLOB tilemaker_src_files
113113
src/tile_data.cpp
114114
src/tilemaker.cpp
115115
src/tile_worker.cpp
116+
src/visvalingam.cpp
116117
src/way_stores.cpp
117118
)
118119
add_executable(tilemaker ${tilemaker_src_files})

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ tilemaker: \
131131
src/tile_data.o \
132132
src/tilemaker.o \
133133
src/tile_worker.o \
134+
src/visvalingam.o \
134135
src/way_stores.o
135136
$(CXX) $(CXXFLAGS) -o tilemaker $^ $(INC) $(LIB) $(LDFLAGS)
136137

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,5 @@ Licenses of third-party libraries:
148148
- [Simple-Web-Server](https://gitlab.com/eidheim/Simple-Web-Server) is licensed under MIT
149149
- [sqlite_modern_cpp](https://github.com/SqliteModernCpp/sqlite_modern_cpp) is licensed under MIT
150150
- [streamvbyte](https://github.com/lemire/streamvbyte) is licensed under Apache 2
151+
- [visvalingam.cpp](https://github.com/felt/tippecanoe/blob/main/visvalingam.cpp) is licensed under MIT
151152
- [vtzero](https://github.com/mapbox/vtzero) is licensed under BSD 2-clause

docs/CONFIGURATION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ You can add optional parameters to layers:
7777
* `simplify_level` - how much to simplify features (in degrees of longitude) on the zoom level `simplify_below-1`
7878
* `simplify_length` - how much to simplify features (in kilometers) on the zoom level `simplify_below-1`, preceding `simplify_level`
7979
* `simplify_ratio` - (optional: the default value is 2.0) the actual simplify level will be `simplify_level * pow(simplify_ratio, (simplify_below-1) - <current zoom>)`
80+
* `simplify_algorithm` - which simplification algorithm to use (defaults to Douglas-Peucker; you can also specify `"visvalingam"`, which can be better for landuse and similar polygons)
8081
* `filter_below` - filter areas by minimum size below this zoom level
8182
* `filter_area` - minimum size (in square degrees of longitude) for the zoom level `filter_below-1`
8283
* `feature_limit` - restrict the number of features written to each tile

include/shared_data.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct LayerDef {
2323
double simplifyLevel;
2424
double simplifyLength;
2525
double simplifyRatio;
26+
uint simplifyAlgo;
2627
uint filterBelow;
2728
double filterArea;
2829
uint combinePolygonsBelow;
@@ -41,6 +42,9 @@ struct LayerDef {
4142
const bool useColumn(std::string &col) {
4243
return allSourceColumns || (std::find(sourceColumns.begin(), sourceColumns.end(), col) != sourceColumns.end());
4344
}
45+
46+
static const uint DOUGLAS_PEUCKER = 0;
47+
static const uint VISVALINGAM = 1;
4448
};
4549

4650
///\brief Defines layers used in map rendering
@@ -53,7 +57,7 @@ class LayerDefinition {
5357

5458
// Define a layer (as read from the .json file)
5559
uint addLayer(std::string name, uint minzoom, uint maxzoom,
56-
uint simplifyBelow, double simplifyLevel, double simplifyLength, double simplifyRatio,
60+
uint simplifyBelow, double simplifyLevel, double simplifyLength, double simplifyRatio, uint simplifyAlgo,
5761
uint filterBelow, double filterArea, uint combinePolygonsBelow, bool sortZOrderAscending,
5862
uint featureLimit, uint featureLimitBelow, bool combinePoints,
5963
const std::string &source,

include/visvalingam.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*! \file */
2+
#ifndef _VISVALINGAM_H
3+
#define _VISVALINGAM_H
4+
5+
// Visvalingam simplify
6+
Linestring simplifyVis(const Linestring &ls, double max_distance);
7+
Polygon simplifyVis(const Polygon &p, double max_distance);
8+
MultiPolygon simplifyVis(const MultiPolygon &mp, double max_distance);
9+
10+
#endif //_VISVALINGAM_H

resources/config-openmaptiles.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"layers": {
33
"place": { "minzoom": 0, "maxzoom": 14 },
4-
"boundary": { "minzoom": 0, "maxzoom": 14, "simplify_below": 12, "simplify_level": 0.0003, "simplify_ratio": 2 },
4+
"boundary": { "minzoom": 0, "maxzoom": 14, "simplify_below": 12, "simplify_level": 0.0003, "simplify_ratio": 2, "simplify_algorithm": "visvalingam" },
55

66
"poi": { "minzoom": 12, "maxzoom": 14 },
77
"poi_detail": { "minzoom": 14, "maxzoom": 14, "write_to": "poi"},
@@ -17,7 +17,7 @@
1717
"building": { "minzoom": 13, "maxzoom": 14 },
1818

1919
"water": { "minzoom": 6, "maxzoom": 14, "simplify_below": 12, "simplify_level": 0.0003, "simplify_ratio": 2},
20-
"ocean": { "minzoom": 0, "maxzoom": 14, "source": "coastline/water_polygons.shp", "filter_below": 12, "filter_area": 0.5, "simplify_below": 13, "simplify_level": 0.0001, "simplify_ratio": 2, "write_to": "water" },
20+
"ocean": { "minzoom": 0, "maxzoom": 14, "source": "coastline/water_polygons.shp", "filter_below": 12, "filter_area": 0.5, "simplify_below": 13, "simplify_level": 0.0001, "simplify_ratio": 2, "simplify_algorithm": "visvalingam", "write_to": "water" },
2121
"water_name": { "minzoom": 14, "maxzoom": 14 },
2222
"water_name_detail": { "minzoom": 14, "maxzoom": 14, "write_to": "water_name" },
2323

src/shared_data.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ void SharedData::writePMTilesBounds() {
135135

136136
// Define a layer (as read from the .json file)
137137
uint LayerDefinition::addLayer(string name, uint minzoom, uint maxzoom,
138-
uint simplifyBelow, double simplifyLevel, double simplifyLength, double simplifyRatio,
138+
uint simplifyBelow, double simplifyLevel, double simplifyLength, double simplifyRatio, uint simplifyAlgo,
139139
uint filterBelow, double filterArea, uint combinePolygonsBelow, bool sortZOrderAscending,
140140
uint featureLimit, uint featureLimitBelow, bool combinePoints,
141141
const std::string &source,
@@ -146,7 +146,7 @@ uint LayerDefinition::addLayer(string name, uint minzoom, uint maxzoom,
146146
const std::string &writeTo) {
147147

148148
bool isWriteTo = !writeTo.empty();
149-
LayerDef layer = { name, minzoom, maxzoom, simplifyBelow, simplifyLevel, simplifyLength, simplifyRatio,
149+
LayerDef layer = { name, minzoom, maxzoom, simplifyBelow, simplifyLevel, simplifyLength, simplifyRatio, simplifyAlgo,
150150
filterBelow, filterArea, combinePolygonsBelow, sortZOrderAscending, featureLimit, featureLimitBelow, combinePoints,
151151
source, sourceColumns, allSourceColumns, indexed, indexName,
152152
std::map<std::string,uint>(), isWriteTo };
@@ -319,6 +319,8 @@ void Config::readConfig(rapidjson::Document &jsonConfig, bool &hasClippingBox, B
319319
int featureLimitBelow= it->value.HasMember("feature_limit_below") ? it->value["feature_limit_below"].GetInt() : (maxZoom+1);
320320
bool combinePoints = it->value.HasMember("combine_points" ) ? it->value["combine_points" ].GetBool() : true;
321321
bool sortZOrderAscending = it->value.HasMember("z_order_ascending") ? it->value["z_order_ascending"].GetBool() : (featureLimit==0);
322+
string algo = it->value.HasMember("simplify_algorithm") ? it->value["simplify_algorithm"].GetString() : "";
323+
uint simplifyAlgo = algo=="visvalingam" ? LayerDef::VISVALINGAM : LayerDef::DOUGLAS_PEUCKER;
322324
string source = it->value.HasMember("source") ? it->value["source"].GetString() : "";
323325
vector<string> sourceColumns;
324326
bool allSourceColumns = false;
@@ -337,7 +339,7 @@ void Config::readConfig(rapidjson::Document &jsonConfig, bool &hasClippingBox, B
337339
string indexName = it->value.HasMember("index_column") ? it->value["index_column"].GetString() : "";
338340

339341
layers.addLayer(layerName, minZoom, maxZoom,
340-
simplifyBelow, simplifyLevel, simplifyLength, simplifyRatio,
342+
simplifyBelow, simplifyLevel, simplifyLength, simplifyRatio, simplifyAlgo,
341343
filterBelow, filterArea, combinePolyBelow, sortZOrderAscending, featureLimit, featureLimitBelow, combinePoints,
342344
source, sourceColumns, allSourceColumns, indexed, indexName,
343345
writeTo);

src/tile_worker.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <vtzero/builder.hpp>
66
#include <signal.h>
77
#include "helpers.h"
8+
#include "visvalingam.h"
89
using namespace std;
910
extern bool verbose;
1011

@@ -100,6 +101,7 @@ void writeMultiLinestring(
100101
const OutputObjectID& oo,
101102
unsigned zoom,
102103
double simplifyLevel,
104+
unsigned simplifyAlgo,
103105
const MultiLinestring& mls
104106
) {
105107
vtzero::linestring_feature_builder fbuilder{vtLayer};
@@ -114,7 +116,11 @@ void writeMultiLinestring(
114116

115117
if (simplifyLevel>0) {
116118
for(auto const &ls: mls) {
117-
tmp.push_back(simplify(ls, simplifyLevel));
119+
if (simplifyAlgo==LayerDef::VISVALINGAM) {
120+
tmp.push_back(simplifyVis(ls, simplifyLevel));
121+
} else {
122+
tmp.push_back(simplify(ls, simplifyLevel));
123+
}
118124
}
119125
toWrite = &tmp;
120126
} else {
@@ -208,11 +214,16 @@ void writeMultiPolygon(
208214
const OutputObjectID& oo,
209215
unsigned zoom,
210216
double simplifyLevel,
217+
unsigned simplifyAlgo,
211218
const MultiPolygon& mp
212219
) {
213220
MultiPolygon current = bbox.scaleGeometry(mp);
214221
if (simplifyLevel>0) {
215-
current = simplify(current, simplifyLevel/bbox.xscale);
222+
if (simplifyAlgo == LayerDef::VISVALINGAM) {
223+
current = simplifyVis(current, simplifyLevel/bbox.xscale);
224+
} else {
225+
current = simplify(current, simplifyLevel/bbox.xscale);
226+
}
216227
geom::remove_spikes(current);
217228
}
218229
if (geom::is_empty(current))
@@ -264,6 +275,7 @@ void ProcessObjects(
264275
OutputObjectsConstIt ooSameLayerEnd,
265276
class SharedData& sharedData,
266277
double simplifyLevel,
278+
unsigned simplifyAlgo,
267279
double filterArea,
268280
bool combinePolygons,
269281
bool combinePoints,
@@ -350,9 +362,9 @@ void ProcessObjects(
350362
}
351363

352364
if (oo.oo.geomType == LINESTRING_ || oo.oo.geomType == MULTILINESTRING_)
353-
writeMultiLinestring(attributeStore, sharedData, vtLayer, bbox, oo, zoom, simplifyLevel, boost::get<MultiLinestring>(g));
365+
writeMultiLinestring(attributeStore, sharedData, vtLayer, bbox, oo, zoom, simplifyLevel, simplifyAlgo, boost::get<MultiLinestring>(g));
354366
else if (oo.oo.geomType == POLYGON_)
355-
writeMultiPolygon(attributeStore, sharedData, vtLayer, bbox, oo, zoom, simplifyLevel, boost::get<MultiPolygon>(g));
367+
writeMultiPolygon(attributeStore, sharedData, vtLayer, bbox, oo, zoom, simplifyLevel, simplifyAlgo, boost::get<MultiPolygon>(g));
356368
}
357369
}
358370
}
@@ -436,7 +448,8 @@ void ProcessLayer(
436448
if (ld.featureLimit>0 && end-ooListSameLayer.first>ld.featureLimit && zoom<ld.featureLimitBelow) end = ooListSameLayer.first+ld.featureLimit;
437449
ProcessObjects(sources[i], attributeStore,
438450
ooListSameLayer.first, end, sharedData,
439-
simplifyLevel, filterArea, zoom < ld.combinePolygonsBelow, ld.combinePoints, zoom, bbox, vtLayer);
451+
simplifyLevel, ld.simplifyAlgo,
452+
filterArea, zoom < ld.combinePolygonsBelow, ld.combinePoints, zoom, bbox, vtLayer);
440453
}
441454
}
442455
if (verbose && std::time(0)-start>3) {

0 commit comments

Comments
 (0)