From 0984df2964a683e9ba2584c09990e4be97f9636d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Mon, 8 Jun 2026 16:54:30 +0200 Subject: [PATCH] Remove unmaintained C++ implementation --- .github/workflows/test.yml | 10 +- .gitignore | 2 - .gitmodules | 3 - .mason | 1 - Makefile | 27 ------ README.md | 14 --- include/mapbox/polylabel.hpp | 178 ----------------------------------- test/test.cpp | 64 ------------- 8 files changed, 1 insertion(+), 298 deletions(-) delete mode 100644 .gitmodules delete mode 160000 .mason delete mode 100644 Makefile delete mode 100644 include/mapbox/polylabel.hpp delete mode 100644 test/test.cpp diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a82688..71b4c9f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,12 +16,4 @@ jobs: run: npm ci - name: Run tests - run: npm test - cpp: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Test C++ - run: make test + run: npm test \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4b76f03..3c3629e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1 @@ -mason_packages node_modules -build diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 2b08bbb..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule ".mason"] - path = .mason - url = https://github.com/mapbox/mason diff --git a/.mason b/.mason deleted file mode 160000 index 1f9a7bb..0000000 --- a/.mason +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1f9a7bb04855ad9ce673e0e27084eddd690b2752 diff --git a/Makefile b/Makefile deleted file mode 100644 index ef73718..0000000 --- a/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -CXXFLAGS += -Iinclude -std=c++14 -Wall -Wextra -Wshadow -Werror -Wno-class-memaccess -g -fPIC - -MASON ?= .mason/mason -VARIANT = variant 1.1.4 -GEOMETRY = geometry 0.9.2 -RAPIDJSON = rapidjson 1.1.0 - -$(MASON): - git submodule update --init - -DEPS = `$(MASON) cflags $(VARIANT)` \ - `$(MASON) cflags $(GEOMETRY)` \ - `$(MASON) cflags $(RAPIDJSON)` - -mason_packages/headers/geometry: $(MASON) - $(MASON) install $(VARIANT) - $(MASON) install $(GEOMETRY) - $(MASON) install $(RAPIDJSON) - -build: - mkdir -p build - -build/test: test/test.cpp include/mapbox/polylabel.hpp build mason_packages/headers/geometry - $(CXX) $(CFLAGS) $(CXXFLAGS) $(DEPS) $< -o $@ - -test: build/test - ./build/test diff --git a/README.md b/README.md index 943660a..95e98ae 100644 --- a/README.md +++ b/README.md @@ -48,20 +48,6 @@ Be careful to pick precision appropriate for the input units. E.g. in case of ge [TypeScript type definitions](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/polylabel) are available via `npm install --save @types/polylabel`. -### C++ Usage - -It is recommended to install polylabel via [mason](https://github.com/mapbox/mason). You will also need to install its dependencies: [geometry.hpp](https://github.com/mapbox/geometry.hpp) and [variant](https://github.com/mapbox/variant). - -```C++ -#include - -int main() { - mapbox::geometry::polygon polygon = readPolygon(); // Get polygon data from somewhere. - mapbox::geometry::point p = mapbox::polylabel(polygon, 1.0); - return 0; -} -``` - #### Ports to other languages - [andrewharvey/geojson-polygon-labels](https://github.com/andrewharvey/geojson-polygon-labels) (CLI) diff --git a/include/mapbox/polylabel.hpp b/include/mapbox/polylabel.hpp deleted file mode 100644 index 384a0f7..0000000 --- a/include/mapbox/polylabel.hpp +++ /dev/null @@ -1,178 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace mapbox { - -namespace detail { - -// get squared distance from a point to a segment -template -T getSegDistSq(const geometry::point& p, - const geometry::point& a, - const geometry::point& b) { - auto x = a.x; - auto y = a.y; - auto dx = b.x - x; - auto dy = b.y - y; - - if (dx != 0 || dy != 0) { - - auto t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy); - - if (t > 1) { - x = b.x; - y = b.y; - - } else if (t > 0) { - x += dx * t; - y += dy * t; - } - } - - dx = p.x - x; - dy = p.y - y; - - return dx * dx + dy * dy; -} - -// signed distance from point to polygon outline (negative if point is outside) -template -auto pointToPolygonDist(const geometry::point& point, const geometry::polygon& polygon) { - bool inside = false; - auto minDistSq = std::numeric_limits::infinity(); - - for (const auto& ring : polygon) { - for (std::size_t i = 0, len = ring.size(), j = len - 1; i < len; j = i++) { - const auto& a = ring[i]; - const auto& b = ring[j]; - - if ((a.y > point.y) != (b.y > point.y) && - (point.x < (b.x - a.x) * (point.y - a.y) / (b.y - a.y) + a.x)) inside = !inside; - - minDistSq = std::min(minDistSq, getSegDistSq(point, a, b)); - } - } - - return (inside ? 1 : -1) * std::sqrt(minDistSq); -} - -template -struct Cell { - Cell(const geometry::point& c_, T h_, const geometry::polygon& polygon) - : c(c_), - h(h_), - d(pointToPolygonDist(c, polygon)), - max(d + h * std::sqrt(2)) - {} - - geometry::point c; // cell center - T h; // half the cell size - T d; // distance from cell center to polygon - T max; // max distance to polygon within a cell -}; - -// get polygon centroid -template -Cell getCentroidCell(const geometry::polygon& polygon) { - T area = 0; - geometry::point c { 0, 0 }; - const auto& ring = polygon.at(0); - - for (std::size_t i = 0, len = ring.size(), j = len - 1; i < len; j = i++) { - const geometry::point& a = ring[i]; - const geometry::point& b = ring[j]; - auto f = a.x * b.y - b.x * a.y; - c.x += (a.x + b.x) * f; - c.y += (a.y + b.y) * f; - area += f * 3; - } - - return Cell(area == 0 ? ring.at(0) : c / area, 0, polygon); -} - -} // namespace detail - -template -geometry::point polylabel(const geometry::polygon& polygon, T precision = 1, bool debug = false) { - using namespace detail; - - // find the bounding box of the outer ring - const geometry::box envelope = geometry::envelope(polygon.at(0)); - - const geometry::point size { - envelope.max.x - envelope.min.x, - envelope.max.y - envelope.min.y - }; - - const T cellSize = std::min(size.x, size.y); - T h = cellSize / 2; - - // a priority queue of cells in order of their "potential" (max distance to polygon) - auto compareMax = [] (const Cell& a, const Cell& b) { - return a.max < b.max; - }; - using Queue = std::priority_queue, std::vector>, decltype(compareMax)>; - Queue cellQueue(compareMax); - - if (cellSize == 0) { - return envelope.min; - } - - // cover polygon with initial cells - for (T x = envelope.min.x; x < envelope.max.x; x += cellSize) { - for (T y = envelope.min.y; y < envelope.max.y; y += cellSize) { - cellQueue.push(Cell({x + h, y + h}, h, polygon)); - } - } - - // take centroid as the first best guess - auto bestCell = getCentroidCell(polygon); - - // second guess: bounding box centroid - Cell bboxCell(envelope.min + size / 2.0, 0, polygon); - if (bboxCell.d > bestCell.d) { - bestCell = bboxCell; - } - - auto numProbes = cellQueue.size(); - while (!cellQueue.empty()) { - // pick the most promising cell from the queue - auto cell = cellQueue.top(); - cellQueue.pop(); - - // update the best cell if we found a better one - if (cell.d > bestCell.d) { - bestCell = cell; - if (debug) std::cout << "found best " << ::round(1e4 * cell.d) / 1e4 << " after " << numProbes << " probes" << std::endl; - } - - // do not drill down further if there's no chance of a better solution - if (cell.max - bestCell.d <= precision) continue; - - // split the cell into four cells - h = cell.h / 2; - cellQueue.push(Cell({cell.c.x - h, cell.c.y - h}, h, polygon)); - cellQueue.push(Cell({cell.c.x + h, cell.c.y - h}, h, polygon)); - cellQueue.push(Cell({cell.c.x - h, cell.c.y + h}, h, polygon)); - cellQueue.push(Cell({cell.c.x + h, cell.c.y + h}, h, polygon)); - numProbes += 4; - } - - if (debug) { - std::cout << "num probes: " << numProbes << std::endl; - std::cout << "best distance: " << bestCell.d << std::endl; - } - - return bestCell.c; -} - -} // namespace mapbox diff --git a/test/test.cpp b/test/test.cpp deleted file mode 100644 index dc35fbf..0000000 --- a/test/test.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include - -#include -#include -#include - -#include -#include -#include -#include - -using namespace mapbox; - -// Use the CrtAllocator, because the MemoryPoolAllocator is broken on ARM -// https://github.com/miloyip/rapidjson/issues/200, 301, 388 -using rapidjson_allocator = rapidjson::CrtAllocator; -using rapidjson_document = rapidjson::GenericDocument, rapidjson_allocator>; -using rapidjson_value = rapidjson::GenericValue, rapidjson_allocator>; - -geometry::polygon fixture(const std::string& path) { - std::ifstream t(path.c_str()); - std::stringstream buffer; - buffer << t.rdbuf(); - - rapidjson_document d; - d.Parse(buffer.str().c_str()); - - geometry::polygon result; - - for (const auto& ringArray : d.GetArray()) { - geometry::linear_ring ring; - for (const auto& point : ringArray.GetArray()) { - ring.push_back({ - point[0].GetDouble(), - point[1].GetDouble() - }); - } - result.push_back(std::move(ring)); - } - - return result; -} - -int main() { - geometry::polygon water1 = fixture("./test/fixtures/water1.json"); - geometry::polygon water2 = fixture("./test/fixtures/water2.json"); - - // finds pole of inaccessibility for water1 and precision 1 - assert(polylabel(water1, 1.0) == geometry::point(3865.85009765625, 2124.87841796875)); - - // finds pole of inaccessibility for water1 and precision 50 - assert(polylabel(water1, 50.0) == geometry::point(3854.296875, 2123.828125)); - - // finds pole of inaccessibility for water2 and default precision 1 - assert(polylabel(water2) == geometry::point(3263.5, 3263.5)); - - // works on degenerate polygons - assert(polylabel(geometry::polygon({{{0, 0}, {1, 0}, {2, 0}, {0, 0}}})) - == geometry::point(0, 0)); - assert(polylabel(geometry::polygon({{{0, 0}, {1, 0}, {1, 1}, {1, 0}, {0, 0}}})) - == geometry::point(0, 0)); - - return 0; -}