Skip to content

Commit

Permalink
Visvalingam-Whyatt simplification (systemed#772)
Browse files Browse the repository at this point in the history
  • Loading branch information
systemed authored Oct 19, 2024
1 parent 868d514 commit 13b841d
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 11 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ file(GLOB tilemaker_src_files
src/tile_data.cpp
src/tilemaker.cpp
src/tile_worker.cpp
src/visvalingam.cpp
src/way_stores.cpp
)
add_executable(tilemaker ${tilemaker_src_files})
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ tilemaker: \
src/tile_data.o \
src/tilemaker.o \
src/tile_worker.o \
src/visvalingam.o \
src/way_stores.o
$(CXX) $(CXXFLAGS) -o tilemaker $^ $(INC) $(LIB) $(LDFLAGS)

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,5 @@ Licenses of third-party libraries:
- [Simple-Web-Server](https://gitlab.com/eidheim/Simple-Web-Server) is licensed under MIT
- [sqlite_modern_cpp](https://github.com/SqliteModernCpp/sqlite_modern_cpp) is licensed under MIT
- [streamvbyte](https://github.com/lemire/streamvbyte) is licensed under Apache 2
- [visvalingam.cpp](https://github.com/felt/tippecanoe/blob/main/visvalingam.cpp) is licensed under MIT
- [vtzero](https://github.com/mapbox/vtzero) is licensed under BSD 2-clause
1 change: 1 addition & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ You can add optional parameters to layers:
* `simplify_level` - how much to simplify features (in degrees of longitude) on the zoom level `simplify_below-1`
* `simplify_length` - how much to simplify features (in kilometers) on the zoom level `simplify_below-1`, preceding `simplify_level`
* `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>)`
* `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)
* `filter_below` - filter areas by minimum size below this zoom level
* `filter_area` - minimum size (in square degrees of longitude) for the zoom level `filter_below-1`
* `feature_limit` - restrict the number of features written to each tile
Expand Down
6 changes: 5 additions & 1 deletion include/shared_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct LayerDef {
double simplifyLevel;
double simplifyLength;
double simplifyRatio;
uint simplifyAlgo;
uint filterBelow;
double filterArea;
uint combinePolygonsBelow;
Expand All @@ -41,6 +42,9 @@ struct LayerDef {
const bool useColumn(std::string &col) {
return allSourceColumns || (std::find(sourceColumns.begin(), sourceColumns.end(), col) != sourceColumns.end());
}

static const uint DOUGLAS_PEUCKER = 0;
static const uint VISVALINGAM = 1;
};

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

// Define a layer (as read from the .json file)
uint addLayer(std::string name, uint minzoom, uint maxzoom,
uint simplifyBelow, double simplifyLevel, double simplifyLength, double simplifyRatio,
uint simplifyBelow, double simplifyLevel, double simplifyLength, double simplifyRatio, uint simplifyAlgo,
uint filterBelow, double filterArea, uint combinePolygonsBelow, bool sortZOrderAscending,
uint featureLimit, uint featureLimitBelow, bool combinePoints,
const std::string &source,
Expand Down
10 changes: 10 additions & 0 deletions include/visvalingam.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*! \file */
#ifndef _VISVALINGAM_H
#define _VISVALINGAM_H

// Visvalingam simplify
Linestring simplifyVis(const Linestring &ls, double max_distance);
Polygon simplifyVis(const Polygon &p, double max_distance);
MultiPolygon simplifyVis(const MultiPolygon &mp, double max_distance);

#endif //_VISVALINGAM_H
4 changes: 2 additions & 2 deletions resources/config-openmaptiles.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"layers": {
"place": { "minzoom": 0, "maxzoom": 14 },
"boundary": { "minzoom": 0, "maxzoom": 14, "simplify_below": 12, "simplify_level": 0.0003, "simplify_ratio": 2 },
"boundary": { "minzoom": 0, "maxzoom": 14, "simplify_below": 12, "simplify_level": 0.0003, "simplify_ratio": 2, "simplify_algorithm": "visvalingam" },

"poi": { "minzoom": 12, "maxzoom": 14 },
"poi_detail": { "minzoom": 14, "maxzoom": 14, "write_to": "poi"},
Expand All @@ -17,7 +17,7 @@
"building": { "minzoom": 13, "maxzoom": 14 },

"water": { "minzoom": 6, "maxzoom": 14, "simplify_below": 12, "simplify_level": 0.0003, "simplify_ratio": 2},
"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" },
"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" },
"water_name": { "minzoom": 14, "maxzoom": 14 },
"water_name_detail": { "minzoom": 14, "maxzoom": 14, "write_to": "water_name" },

Expand Down
8 changes: 5 additions & 3 deletions src/shared_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ void SharedData::writePMTilesBounds() {

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

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

layers.addLayer(layerName, minZoom, maxZoom,
simplifyBelow, simplifyLevel, simplifyLength, simplifyRatio,
simplifyBelow, simplifyLevel, simplifyLength, simplifyRatio, simplifyAlgo,
filterBelow, filterArea, combinePolyBelow, sortZOrderAscending, featureLimit, featureLimitBelow, combinePoints,
source, sourceColumns, allSourceColumns, indexed, indexName,
writeTo);
Expand Down
23 changes: 18 additions & 5 deletions src/tile_worker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <vtzero/builder.hpp>
#include <signal.h>
#include "helpers.h"
#include "visvalingam.h"
using namespace std;
extern bool verbose;

Expand Down Expand Up @@ -100,6 +101,7 @@ void writeMultiLinestring(
const OutputObjectID& oo,
unsigned zoom,
double simplifyLevel,
unsigned simplifyAlgo,
const MultiLinestring& mls
) {
vtzero::linestring_feature_builder fbuilder{vtLayer};
Expand All @@ -114,7 +116,11 @@ void writeMultiLinestring(

if (simplifyLevel>0) {
for(auto const &ls: mls) {
tmp.push_back(simplify(ls, simplifyLevel));
if (simplifyAlgo==LayerDef::VISVALINGAM) {
tmp.push_back(simplifyVis(ls, simplifyLevel));
} else {
tmp.push_back(simplify(ls, simplifyLevel));
}
}
toWrite = &tmp;
} else {
Expand Down Expand Up @@ -208,11 +214,16 @@ void writeMultiPolygon(
const OutputObjectID& oo,
unsigned zoom,
double simplifyLevel,
unsigned simplifyAlgo,
const MultiPolygon& mp
) {
MultiPolygon current = bbox.scaleGeometry(mp);
if (simplifyLevel>0) {
current = simplify(current, simplifyLevel/bbox.xscale);
if (simplifyAlgo == LayerDef::VISVALINGAM) {
current = simplifyVis(current, simplifyLevel/bbox.xscale);
} else {
current = simplify(current, simplifyLevel/bbox.xscale);
}
geom::remove_spikes(current);
}
if (geom::is_empty(current))
Expand Down Expand Up @@ -264,6 +275,7 @@ void ProcessObjects(
OutputObjectsConstIt ooSameLayerEnd,
class SharedData& sharedData,
double simplifyLevel,
unsigned simplifyAlgo,
double filterArea,
bool combinePolygons,
bool combinePoints,
Expand Down Expand Up @@ -350,9 +362,9 @@ void ProcessObjects(
}

if (oo.oo.geomType == LINESTRING_ || oo.oo.geomType == MULTILINESTRING_)
writeMultiLinestring(attributeStore, sharedData, vtLayer, bbox, oo, zoom, simplifyLevel, boost::get<MultiLinestring>(g));
writeMultiLinestring(attributeStore, sharedData, vtLayer, bbox, oo, zoom, simplifyLevel, simplifyAlgo, boost::get<MultiLinestring>(g));
else if (oo.oo.geomType == POLYGON_)
writeMultiPolygon(attributeStore, sharedData, vtLayer, bbox, oo, zoom, simplifyLevel, boost::get<MultiPolygon>(g));
writeMultiPolygon(attributeStore, sharedData, vtLayer, bbox, oo, zoom, simplifyLevel, simplifyAlgo, boost::get<MultiPolygon>(g));
}
}
}
Expand Down Expand Up @@ -436,7 +448,8 @@ void ProcessLayer(
if (ld.featureLimit>0 && end-ooListSameLayer.first>ld.featureLimit && zoom<ld.featureLimitBelow) end = ooListSameLayer.first+ld.featureLimit;
ProcessObjects(sources[i], attributeStore,
ooListSameLayer.first, end, sharedData,
simplifyLevel, filterArea, zoom < ld.combinePolygonsBelow, ld.combinePoints, zoom, bbox, vtLayer);
simplifyLevel, ld.simplifyAlgo,
filterArea, zoom < ld.combinePolygonsBelow, ld.combinePoints, zoom, bbox, vtLayer);
}
}
if (verbose && std::time(0)-start>3) {
Expand Down
Loading

0 comments on commit 13b841d

Please sign in to comment.