// Copyright (c) 2020, 2025, Oracle and/or its affiliates.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 2.0,
// as published by the Free Software Foundation.
//
// This program is designed to work with certain software (including
// but not limited to OpenSSL) that is licensed under separate terms,
// as designated in a particular file or component or in included license
// documentation. The authors of MySQL hereby grant you an additional
// permission to link the program and your derivative works with the
// separately licensed software that they have either included with
// the program or referenced in the documentation.
//
// This program 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, version 2.0, for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
/// @file
///
/// This file implements the intersection functor.
#include // std::unique_ptr
#include
#include "sql/gis/gc_utils.h"
#include "sql/gis/geometries.h"
#include "sql/gis/geometries_traits.h"
#include "sql/gis/intersection_functor.h"
#include "sql/gis/so_utils.h"
#include "sql/gis/union_functor.h"
namespace bg = boost::geometry;
namespace gis {
static bool is_collection(const Geometry *g) {
switch (g->type()) {
case Geometry_type::kPoint:
case Geometry_type::kLinestring:
case Geometry_type::kPolygon:
return false;
default:
return true;
}
}
template
static auto remove_overlapping_mpt_mls(MPt const &mpt, MLs const &mls,
Geometrycollection &result) {
std::unique_ptr difference = std::make_unique();
bg::difference(mpt, mls, *difference);
for (auto ls : mls) result.push_back(ls);
for (auto pt : *difference) result.push_back(pt);
}
template
static auto remove_overlapping_mpt_mls_mpy(MPt const &mpt, MLs const &mls,
MPy const &mpy,
Geometrycollection &result) {
std::unique_ptr mpt_mls_difference = std::make_unique();
bg::difference(mpt, mls, *mpt_mls_difference);
std::unique_ptr mpt_mls_mpy_difference = std::make_unique();
bg::difference(*mpt_mls_difference, mpy, *mpt_mls_mpy_difference);
if (!mpt_mls_mpy_difference->is_empty()) {
if (mpt_mls_mpy_difference->size() == 1) {
result.push_back((*mpt_mls_mpy_difference)[0]);
} else {
result.push_back(*mpt_mls_mpy_difference);
}
}
std::unique_ptr mls_mpy_difference = std::make_unique();
bg::difference(mls, mpy, *mls_mpy_difference);
if (!mls_mpy_difference->is_empty()) {
if (mls_mpy_difference->size() == 1) {
result.push_back((*mls_mpy_difference)[0]);
} else {
result.push_back(*mls_mpy_difference);
}
}
if (!mpy.is_empty()) {
if (mpy.size() == 1) {
result.push_back(mpy[0]);
} else {
result.push_back(mpy);
}
}
}
template
static auto apply_bg_intersection(Geometry1 const &g1, Geometry2 const &g2) {
std::unique_ptr<:geometrycollection> result(
gis::Geometrycollection::create_geometrycollection(
g1.coordinate_system()));
std::tuple bg_result;
bg::intersection(g1, g2, bg_result);
remove_overlapping_mpt_mls_mpy(std::get<0>(bg_result), std::get<1>(bg_result),
std::get<2>(bg_result), *result);
return result;
}
template
static auto apply_bg_intersection(Geometry1 const &g1, Geometry2 const &g2,
Strategy const &strategy) {
std::unique_ptr<:geometrycollection> result(
gis::Geometrycollection::create_geometrycollection(
g1.coordinate_system()));
std::tuple bg_result;
bg::intersection(g1, g2, bg_result, strategy);
remove_overlapping_mpt_mls_mpy(std::get<0>(bg_result), std::get<1>(bg_result),
std::get<2>(bg_result), *result);
return result;
}
template
static auto apply_bg_brute_force_intersection(Geometry1 const &g1,
Geometry2 const &g2) {
std::unique_ptr<:geometrycollection> result(
gis::Geometrycollection::create_geometrycollection(
g1.coordinate_system()));
MPt bg_result_mp;
bg::intersection(g1, g2, bg_result_mp);
MLs bg_result_ml;
bg::intersection(g1, g2, bg_result_ml);
remove_overlapping_mpt_mls(bg_result_mp, bg_result_ml, *result);
return result;
}
template
static auto apply_bg_brute_force_intersection(Geometry1 const &g1,
Geometry2 const &g2,
Strategy const &strategy) {
std::unique_ptr<:geometrycollection> result(
gis::Geometrycollection::create_geometrycollection(
g1.coordinate_system()));
MPt bg_result_mp;
bg::intersection(g1, g2, bg_result_mp, strategy);
MLs bg_result_ml;
bg::intersection(g1, g2, bg_result_ml, strategy);
remove_overlapping_mpt_mls(bg_result_mp, bg_result_ml, *result);
return result;
}
template
static std::unique_ptr typed_geometry_collection_apply_intersection(
const Intersection &f, const Geometrycollection *g1, const Geometry *g2) {
std::unique_ptr result = std::make_unique();
if (g1->is_empty() || g2->is_empty()) return std::make_unique(*result);
std::unique_ptr mpt;
std::unique_ptr mls;
std::unique_ptr mpy;
split_gc(g1, &mpt, &mls, &mpy);
gc_union(f.semi_major(), f.semi_minor(), &mpt, &mls, &mpy);
if (!mpy->is_empty()) {
std::unique_ptr mpy_result = f(mpy.get(), g2);
if (mpt->is_empty() && mls->is_empty()) {
return mpy_result;
}
if (is_collection(mpy_result.get())) {
Geometrycollection *gc_mpy_result =
down_cast(mpy_result.get());
for (size_t i = 0; i < gc_mpy_result->size(); i++)
result->push_back((*gc_mpy_result)[i]);
} else
result->push_back(*mpy_result);
}
if (!mls->is_empty()) {
std::unique_ptr mls_result = f(mls.get(), g2);
if (mpy->is_empty() && mpt->is_empty()) {
return mls_result;
}
if (is_collection(mls_result.get())) {
Geometrycollection *gc_mls_result =
down_cast(mls_result.get());
for (size_t i = 0; i < gc_mls_result->size(); i++)
result->push_back((*gc_mls_result)[i]);
} else
result->push_back(*mls_result);
}
if (!mpt->is_empty()) {
std::unique_ptr mpt_result = f(mpt.get(), g2);
if (mpy->is_empty() && mls->is_empty()) {
return mpt_result;
}
if (is_collection(mpt_result.get())) {
Geometrycollection *gc_mpt_result =
down_cast(mpt_result.get());
for (size_t i = 0; i < gc_mpt_result->size(); i++)
result->push_back((*gc_mpt_result)[i]);
} else
result->push_back(*mpt_result);
}
return std::make_unique(*result);
}
/// Apply a Intersection functor to two geometries, where at least one is a
/// geometry collection. Return the intersection of the two geometries.
///
/// @param f Functor to apply.
/// @param g1 First geometry, of type Geometrycollection.
/// @param g2 Second geometry.
///
/// @retval unique pointer to a Geometry.
static std::unique_ptr geometry_collection_apply_intersection(
const Intersection &f, const Geometrycollection *g1, const Geometry *g2) {
switch (g1->coordinate_system()) {
case Coordinate_system::kCartesian: {
return typed_geometry_collection_apply_intersection<
Cartesian_geometrycollection, Cartesian_multipoint,
Cartesian_multilinestring, Cartesian_multipolygon>(f, g1, g2);
}
case Coordinate_system::kGeographic: {
return typed_geometry_collection_apply_intersection<
Geographic_geometrycollection, Geographic_multipoint,
Geographic_multilinestring, Geographic_multipolygon>(f, g1, g2);
}
}
assert(false);
// This should never happen.
throw not_implemented_exception::for_non_projected(*g1);
}
Intersection::Intersection(double semi_major, double semi_minor)
: m_semi_major(semi_major),
m_semi_minor(semi_minor),
m_geographic_pl_pa_strategy(
bg::srs::spheroid(semi_major, semi_minor)),
m_geographic_ll_la_aa_strategy(
bg::srs::spheroid(semi_major, semi_minor)) {}
std::unique_ptr Intersection::operator()(const Geometry *g1,
const Geometry *g2) const {
std::unique_ptr result = apply(*this, g1, g2);
remove_duplicates(this->semi_major(), this->semi_minor(), &result);
narrow_geometry(&result);
return result;
}
std::unique_ptr Intersection::eval(const Geometry *g1,
const Geometry *g2) const {
assert(false);
throw not_implemented_exception::for_non_projected(*g1, *g2);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Cartesian_point, *)
std::unique_ptr Intersection::eval(const Cartesian_point *g1,
const Cartesian_point *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_point *g1, const Cartesian_linestring *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_point *g1, const Cartesian_polygon *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_point *g1, const Cartesian_multipoint *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_point *g1, const Cartesian_multilinestring *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_point *g1, const Cartesian_multipolygon *g2) const {
return apply_bg_intersection(*g1, *g2);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Cartesian_linestring, *)
std::unique_ptr Intersection::eval(const Cartesian_linestring *g1,
const Cartesian_point *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_linestring *g1, const Cartesian_linestring *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_linestring *g1, const Cartesian_polygon *g2) const {
// TODO: fix this in boost geometry
return apply_bg_brute_force_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_linestring *g1, const Cartesian_multipoint *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_linestring *g1, const Cartesian_multilinestring *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_linestring *g1, const Cartesian_multipolygon *g2) const {
// TODO: fix in boost geometry
return apply_bg_brute_force_intersection(*g1, *g2);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Cartesian_polygon, *)
std::unique_ptr Intersection::eval(const Cartesian_polygon *g1,
const Cartesian_point *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_polygon *g1, const Cartesian_linestring *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_polygon *g1, const Cartesian_polygon *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_polygon *g1, const Cartesian_multipoint *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_polygon *g1, const Cartesian_multilinestring *g2) const {
// TODO: fix in boost geometry
return apply_bg_brute_force_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_polygon *g1, const Cartesian_multipolygon *g2) const {
return apply_bg_intersection(*g1, *g2);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Cartesian_geometrycollection, *)
std::unique_ptr Intersection::eval(
const Cartesian_geometrycollection *g1, const Geometry *g2) const {
return geometry_collection_apply_intersection(*this, g1, g2);
}
std::unique_ptr Intersection::eval(
const Geometry *g1, const Cartesian_geometrycollection *g2) const {
return geometry_collection_apply_intersection(*this, g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_geometrycollection *g1,
const Cartesian_geometrycollection *g2) const {
return geometry_collection_apply_intersection(*this, g1, g2);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Cartesian_multipoint, *)
std::unique_ptr Intersection::eval(const Cartesian_multipoint *g1,
const Cartesian_point *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_multipoint *g1, const Cartesian_linestring *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_multipoint *g1, const Cartesian_polygon *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_multipoint *g1, const Cartesian_multipoint *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_multipoint *g1, const Cartesian_multilinestring *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_multipoint *g1, const Cartesian_multipolygon *g2) const {
return apply_bg_intersection(*g1, *g2);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Cartesian_multilinestring, *)
std::unique_ptr Intersection::eval(
const Cartesian_multilinestring *g1, const Cartesian_point *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_multilinestring *g1, const Cartesian_linestring *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_multilinestring *g1, const Cartesian_polygon *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_multilinestring *g1, const Cartesian_multipoint *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_multilinestring *g1,
const Cartesian_multilinestring *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Cartesian_multilinestring *g1,
const Cartesian_multipolygon *g2) const {
// TODO: fix this in boost geometry
return apply_bg_brute_force_intersection(*g1, *g2);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Cartesian_multipolygon, *)
std::unique_ptr Intersection::eval(const Cartesian_multipolygon *g1,
const Cartesian_point *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_multipolygon *g1, const Cartesian_linestring *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_multipolygon *g1, const Cartesian_polygon *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_multipolygon *g1, const Cartesian_multipoint *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_multipolygon *g1,
const Cartesian_multilinestring *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Cartesian_multipolygon *g1, const Cartesian_multipolygon *g2) const {
return apply_bg_intersection(*g1, *g2);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Geographic_point, *)
std::unique_ptr Intersection::eval(const Geographic_point *g1,
const Geographic_point *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Geographic_point *g1, const Geographic_linestring *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_pl_pa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_point *g1, const Geographic_polygon *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_pl_pa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_point *g1, const Geographic_multipoint *g2) const {
return apply_bg_intersection(*g1, *g2);
}
std::unique_ptr Intersection::eval(
const Geographic_point *g1, const Geographic_multilinestring *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_pl_pa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_point *g1, const Geographic_multipolygon *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_pl_pa_strategy);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Geographic_linestring, *)
std::unique_ptr Intersection::eval(const Geographic_linestring *g1,
const Geographic_point *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_linestring *g1, const Geographic_linestring *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_ll_la_aa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_linestring *g1, const Geographic_polygon *g2) const {
// TODO: fix this in boost geometry
return apply_bg_brute_force_intersection(
*g1, *g2, m_geographic_ll_la_aa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_linestring *g1, const Geographic_multipoint *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_pl_pa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_linestring *g1,
const Geographic_multilinestring *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_ll_la_aa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_linestring *g1, const Geographic_multipolygon *g2) const {
// TODO: fix this in boost geometry
return apply_bg_brute_force_intersection(
*g1, *g2, m_geographic_ll_la_aa_strategy);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Geographic_polygon, *)
std::unique_ptr Intersection::eval(const Geographic_polygon *g1,
const Geographic_point *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_polygon *g1, const Geographic_linestring *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_polygon *g1, const Geographic_polygon *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_ll_la_aa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_polygon *g1, const Geographic_multipoint *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_pl_pa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_polygon *g1, const Geographic_multilinestring *g2) const {
// TODO: fix this in boost geometry
return apply_bg_brute_force_intersection(
*g1, *g2, m_geographic_ll_la_aa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_polygon *g1, const Geographic_multipolygon *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_ll_la_aa_strategy);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Geographic_geometrycollection, *)
std::unique_ptr Intersection::eval(
const Geographic_geometrycollection *g1, const Geometry *g2) const {
return geometry_collection_apply_intersection(*this, g1, g2);
}
std::unique_ptr Intersection::eval(
const Geometry *g1, const Geographic_geometrycollection *g2) const {
return geometry_collection_apply_intersection(*this, g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_geometrycollection *g1,
const Geographic_geometrycollection *g2) const {
return geometry_collection_apply_intersection(*this, g1, g2);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Geographic_multipoint, *)
std::unique_ptr Intersection::eval(const Geographic_multipoint *g1,
const Geographic_point *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_multipoint *g1, const Geographic_linestring *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_multipoint *g1, const Geographic_polygon *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_multipoint *g1, const Geographic_multipoint *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_pl_pa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_multipoint *g1,
const Geographic_multilinestring *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_pl_pa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_multipoint *g1, const Geographic_multipolygon *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_pl_pa_strategy);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Geographic_multilinestring, *)
std::unique_ptr Intersection::eval(
const Geographic_multilinestring *g1, const Geographic_point *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_multilinestring *g1,
const Geographic_linestring *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_multilinestring *g1, const Geographic_polygon *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_multilinestring *g1,
const Geographic_multipoint *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_multilinestring *g1,
const Geographic_multilinestring *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_ll_la_aa_strategy);
}
std::unique_ptr Intersection::eval(
const Geographic_multilinestring *g1,
const Geographic_multipolygon *g2) const {
// TODO: fix this in boost geometry
return apply_bg_brute_force_intersection(
*g1, *g2, m_geographic_ll_la_aa_strategy);
}
//////////////////////////////////////////////////////////////////////////////
// intersection(Geographic_multipolygon, *)
std::unique_ptr Intersection::eval(const Geographic_multipolygon *g1,
const Geographic_point *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_multipolygon *g1, const Geographic_linestring *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_multipolygon *g1, const Geographic_polygon *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_multipolygon *g1, const Geographic_multipoint *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_multipolygon *g1,
const Geographic_multilinestring *g2) const {
return (*this)(g2, g1);
}
std::unique_ptr Intersection::eval(
const Geographic_multipolygon *g1,
const Geographic_multipolygon *g2) const {
return apply_bg_intersection(
*g1, *g2, m_geographic_ll_la_aa_strategy);
}
} // namespace gis