Обрезка части многоугольника (кольца) из многоугольника (кольца) с помощью ускоренной геометрии

Пытаюсь обрезать кольцо снизу вверх (50%), но результаты не работают так, как я ожидал.

Мое решение

using bg_point_type = boost::geometry::model::d2::point_xy<double>;
using bg_polygon_type = boost::geometry::model::ring<bg_point_type>;
//Find the envelope of the region of the object
bg_polygon_type obj_region = //... it is a rectangle
boost::geometry::model::box<bg_point_type> envelope_box;
boost::geometry::envelope(obj_region, envelope_box);

auto const top_y = envelope_box.min_corner().y();
auto const bottom_y = envelope_box.max_corner().y();
auto const left = envelope_box.min_corner().x();
auto const right = envelope_box.max_corner().x();
//with min_corner and max_corner, we can know the top of the ring
auto const line_y = bottom_y - (bottom_y - top_y) * ratio;

//find the intersection of the line and the polygon
boost::geometry::model::linestring<bg_point_type> const line{{left, line_y}, {right, line_y}};
std::vector<bg_point_type> intersect;
boost::geometry::intersection(obj_region, line, intersect);

//remove those points higher than top
bg_polygon_type result = obj_region;
auto it = std::remove_if (std::begin(result ), std::end(result ), [=](auto const &pt)
{
    return pt.y() < line_y;
});
result .erase(it, std::end(result ));  
//insert the intersect points into the results
std::copy(std::begin(intersect), std::end(intersect), std::back_inserter(result));    
boost::geometry::correct(result);

Это решение дало мне нужную точку, но не дало мне нужной формы.

С точками "0.1,0.1,0.1,0.5,0.5,0.5,0.5,0.1,0.1,0.1" у меня есть такое кольцо (для простоты я выбрал прямоугольник в качестве примера)

Обрезка части многоугольника (кольца) из многоугольника (кольца) с помощью ускоренной геометрии

После того, как я обрезал кольцо, я надеюсь, что это может быть (с очками, 0,1,0,5,0,1,0,3,0,5,0,3,0,5,0,5,0,1,0,5)

Обрезка части многоугольника (кольца) из многоугольника (кольца) с помощью ускоренной геометрии

Но это дало мне 0,1,0,5,0,5,0,5,0,1,0,3,0,5,0,3,0,1,0,5

Обрезка части многоугольника (кольца) из многоугольника (кольца) с помощью ускоренной геометрии

Проблема в том, что «правильный» многоугольник не уникален, как я могу получить желаемые результаты, если он слишком длинный / слишком сложный, чтобы сказать здесь, какие ключевые слова мне следует искать? Я пробовал «обрезать многоугольник», «обрезать часть многоугольника», «обрезать многоугольник из многоугольника» и т. д. Спасибо

Изменить 1: obj_region - простой многоугольник

Изменить 2: проблема решения sehe

Я меняю функцию crop_box на обрезку снизу вверх и ожидаю, что api пересечений даст мне тот же результат, но вместо этого он вернет странные результаты.

template <typename G>
bg_box_type crop_box(G const& geom, double ratio) {
    bg_box_type env;
    bg::envelope(geom, env);

    auto const miny = env.min_corner().y();
    auto const height = env.max_corner().y() - miny;

    env.min_corner().set<1>(env.max_corner().y() - height*ratio);
    return env;
}

Ввод: ПОЛИГОН ((100.0 100.0 100.0 200.0 200.0 200.0 200.0 100.0 100.0 100.0)) Выход: POLYGON ((100 200,100 150,200 200,200 150,100 200)) - недопустимый Вывод после правильного: POLYGON ((100 200,100 150,200 200,200 150,100 200)) - неверно

Полные исходные коды

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/arithmetic/arithmetic.hpp>
#include <boost/geometry/algorithms/for_each.hpp>
#include <boost/geometry/algorithms/envelope.hpp>
#include <boost/geometry/algorithms/intersection.hpp>
#include <boost/geometry/io/io.hpp>
#include <iostream>
#include <fstream>

namespace bg = boost::geometry;

using bg_point_type = bg::model::d2::point_xy<double>;
using bg_polygon_type = bg::model::ring<bg_point_type>;
using bg_box_type = bg::model::box<bg_point_type>;

template <typename G>
bg_box_type crop_box(G const& geom, double ratio) {
    bg_box_type env;
    bg::envelope(geom, env);

    auto const miny = env.min_corner().y();
    auto const height = env.max_corner().y() - miny;

    env.min_corner().set<1>(env.max_corner().y() - height*ratio);
    return env;
}

template <typename G>
void diags(std::string name, G& geom) {
    std::cout << name << ": " << bg::wkt(geom) << "\n";

    std::string reason;
    if (!bg::is_valid(geom, reason)) {
        std::cout << name << ": " << reason << "\n";

        bg::correct(geom);

        std::cout << bg::wkt(geom) << "\n";
        if (!bg::is_valid(geom, reason)) {
            std::cout << name << " corrected: " << reason << "\n";
        }
    }
}

int main() {
    bool const visualize = true;

    //Find the envelope of the region of the object
    bg_polygon_type obj_region;
    bg::read_wkt("POLYGON((100.0 100.0,100.0 200.0,200.0 200.0,200.0 100.0,100.0 100.0))", obj_region);

    diags("Input", obj_region);

    bg_polygon_type out;
    bg::intersection(crop_box(obj_region, 0.5), obj_region, out);

    diags("output", out);

    std::cout << "Output: " << bg::wkt(out) << "\n";

    if (visualize) {
        std::ofstream svg("svg.svg");
        boost::geometry::svg_mapper<bg_point_type> mapper(svg, 600, 600);
        mapper.add(obj_region);
        mapper.add(out);

        mapper.map(obj_region, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(204,153,0);stroke-width:2");
        mapper.map(out, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
    }
}

Обрезка части многоугольника (кольца) из многоугольника (кольца) с помощью ускоренной геометрии

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
311
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы просто хотите пересечь исходный region_obj с нижней половиной его envelope_box.

Вместо этого, найдя только верхнюю линию пересечения и удалив насильно случайным образом точки внутри многоугольника, вы создавали недопустимый многоугольник:

Live On Coliru

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <boost/geometry/algorithms/envelope.hpp>
#include <boost/geometry/algorithms/intersection.hpp>
#include <boost/geometry/io/io.hpp>
#include <iostream>

namespace bg = boost::geometry;

using bg_point_type = bg::model::d2::point_xy<double>;
using bg_polygon_type = bg::model::ring<bg_point_type>;

template <typename G>
void diags(std::string name, G& geom) {
    std::cout << name << ": " << bg::wkt(geom) << "\n";

    std::string reason;
    if (!bg::is_valid(geom, reason)) {
        std::cout << name << " invalid: " << reason << "\n";

        bg::correct(geom);

        std::cout << name << " corrected: " << bg::wkt(geom) << "\n";
        if (!bg::is_valid(geom, reason)) {
            std::cout << name << " invalid: " << reason << "\n";
        }
    }
}

int main() {
    double const ratio = 0.5;

    //Find the envelope of the region of the object
    bg_polygon_type obj_region;
    bg::read_wkt("POLYGON((0.1 0.1,0.1 0.5,0.5 0.5,0.5 0.1,0.1 0.1))", obj_region);

    diags("Input", obj_region);

    bg::model::box<bg_point_type> envelope_box;
    bg::envelope(obj_region, envelope_box);

    auto const top_y    = envelope_box.min_corner().y();
    auto const bottom_y = envelope_box.max_corner().y();
    auto const left     = envelope_box.min_corner().x();
    auto const right    = envelope_box.max_corner().x();

    //with min_corner and max_corner, we can know the top of the ring
    auto const line_y   = bottom_y - (bottom_y - top_y) * ratio;

    //find the intersection of the line and the polygon
    bg::model::linestring<bg_point_type> const line{{left, line_y}, {right, line_y}};
    bg::model::multi_point<bg_point_type> intersect;
    bg::intersection(obj_region, line, intersect);

    std::cout << bg::wkt(intersect) << "\n";

    //remove those points higher than top
    bg_polygon_type result = obj_region;
    auto it = std::remove_if (std::begin(result ), std::end(result ), [=](auto const &pt) { return pt.y() < line_y; });
    result.erase(it, std::end(result ));  

    //insert the intersect points into the results
    std::copy(std::begin(intersect), std::end(intersect), std::back_inserter(result));    
    diags("Result", result);
}

Печать

Input: POLYGON((0.1 0.1,0.1 0.5,0.5 0.5,0.5 0.1,0.1 0.1))
MULTIPOINT((0.1 0.3),(0.5 0.3))
Result: POLYGON((0.1 0.5,0.5 0.5,0.1 0.3,0.5 0.3))
Result invalid: Geometry is defined as closed but is open
Result corrected: POLYGON((0.1 0.5,0.5 0.5,0.1 0.3,0.5 0.3,0.1 0.5))
Result invalid: Geometry has invalid self-intersections. A self-intersection point was found at (0.3, 0.4); method: i; operations: u/i; segment IDs {source, multi, ring, segment}: {0, -1, -1, 1}/{0, -1, -1, 3}

Исправляем это

Просто спрашивая, что вы имеете в виду:

bg_polygon_type obj_region;
bg::read_wkt("POLYGON((0.1 0.1,0.1 0.5,0.5 0.5,0.5 0.1,0.1 0.1))", obj_region);

diags("Input", obj_region); // check validity/attempt correction

bg_polygon_type out;
bg::intersection(crop_box(obj_region, 0.5), obj_region, out);

std::cout << "Output: " << bg::wkt(out) << "\n";

Конечно, определите crop_box таким же образом, как и у вас (только, достигните конверта обрезки, а не multi_point):

template <typename G>
bg_box_type crop_box(G const& geom, double ratio) {
    bg_box_type env;
    bg::envelope(geom, env);

    auto miny = env.min_corner().y();
    auto height = env.max_corner().y() - miny;

    env.max_corner().set<1>(miny + height*ratio);
    return env;
}

Демо

Также добавляем визуализацию:

For visualization we scale all input by 100x

Live On Coliru

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/arithmetic/arithmetic.hpp>
#include <boost/geometry/algorithms/for_each.hpp>
#include <boost/geometry/algorithms/envelope.hpp>
#include <boost/geometry/algorithms/intersection.hpp>
#include <boost/geometry/io/io.hpp>
#include <iostream>
#include <fstream>

namespace bg = boost::geometry;

using bg_point_type = bg::model::d2::point_xy<double>;
using bg_ring_type = bg::model::ring<bg_point_type>;
using bg_polygon_type = bg::model::polygon<bg_point_type>;
using bg_multipolygon_type = bg::model::multi_polygon<bg_polygon_type>;
using bg_box_type = bg::model::box<bg_point_type>;

template <typename G>
bg_box_type crop_box(G const& geom, double ratio) {
    bg_box_type env;
    bg::envelope(geom, env);

    auto const maxy   = env.max_corner().y();
    auto const height = maxy - env.min_corner().y() ;

    env.min_corner().set<1>(maxy - height*ratio);
    return env;
}

template <typename G>
void diags(std::string name, G& geom) {
    std::cout << name << ": " << bg::wkt(geom) << "\n";

    std::string reason;
    if (!bg::is_valid(geom, reason)) {
        std::cout << name << ": " << reason << "\n";

        bg::correct(geom);

        std::cout << bg::wkt(geom) << "\n";
        if (!bg::is_valid(geom, reason)) {
            std::cout << name << " corrected: " << reason << "\n";
        }
    }
}

int main(int argc, char** argv) {
    bool const visualize = argc>1 && argv[1]==std::string("-v");

    //Find the envelope of the region of the object
    bg_polygon_type obj_region;
    bg::read_wkt("POLYGON((0.1 0.1,0.1 0.5,0.5 0.5,0.5 0.1,0.1 0.1))", obj_region);

    if (visualize) {
        bg::for_each_point(obj_region, [](bg_point_type& p) { bg::multiply_value(p, 100.0); });
    }

    diags("Input", obj_region);

    bg_multipolygon_type out;
    bg::intersection(crop_box(obj_region, 0.5), obj_region, out);
    diags("Output", out);

    if (visualize) {
        std::ofstream svg("svg.svg");
        boost::geometry::svg_mapper<bg_point_type> mapper(svg, 600, 600);
        mapper.add(obj_region);
        mapper.add(out);

        mapper.map(obj_region, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(204,153,0);stroke-width:2");
        mapper.map(out, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
    }
}

Печать

Input: POLYGON((0.1 0.1,0.1 0.5,0.5 0.5,0.5 0.1,0.1 0.1))
Output: MULTIPOLYGON(((0.1 0.5,0.5 0.5,0.5 0.3,0.1 0.3,0.1 0.5)))

Или когда возможность визуализации:

Input: POLYGON((10 10,10 50,50 50,50 10,10 10))
Output: MULTIPOLYGON(((10 50,50 50,50 30,10 30,10 50)))

И следующий вывод svg:

Я обнаружил проблему при использовании вашего решения, оно не сработает, если я изменю min_coordinate, если у вас есть время, проверьте мой пост, я редактирую и добавляю новую информацию, спасибо.

StereoMatching 28.05.2018 12:46

Да, ой, мне плохо. состояние документов, что GeometryOut, в этом случае должен быть многогранником. Исправил мой ответ, теперь вывести i.imgur.com/KonUEkj.png

sehe 28.05.2018 13:46

Спасибо, это работает. Не могли бы вы указать мне ссылку на документ? Я хочу знать, почему это должно быть multi_polygon.

StereoMatching 28.05.2018 14:33

Спасибо, не заметил. Я читал его раньше, но не полностью понимал документацию, согласно вашим кодам и документу, я предполагаю, что он говорит: «Если вы хотите найти многоугольник пересечения, тип вывода должен быть multi_polygon».

StereoMatching 28.05.2018 15:08

Другие вопросы по теме