// Copyright (c) 2017, 2024, 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 mbr_disjoint function. #include "sql/gis/mbr_utils.h" #include // std::isnan #include #include #include "sql/dd/types/spatial_reference_system.h" // dd::Spatial_reference_system #include "sql/gis/box.h" #include "sql/gis/box_traits.h" #include "sql/gis/geometries.h" #include "sql/gis/geometries_cs.h" #include "sql/gis/geometries_traits.h" #include "sql/gis/geometry_extraction.h" #include "sql/item.h" #include "template_utils.h" // down_cast namespace bg = boost::geometry; namespace gis { bool mbrs_are_equal(Box const &mbr1, Box const &mbr2) { assert(mbr1.coordinate_system() == mbr2.coordinate_system()); switch (mbr1.coordinate_system()) { case Coordinate_system::kCartesian: return bg::equals(*down_cast(&mbr1), *down_cast(&mbr2)); case Coordinate_system::kGeographic: return bg::equals(*down_cast(&mbr1), *down_cast(&mbr2)); } assert(false); /* purecov: inspected */ return false; /* purecov: inspected */ } bool mbr_is_empty(Box const &mbr) { return std::isnan(mbr.min_corner().x()) && std::isnan(mbr.min_corner().y()) && std::isnan(mbr.max_corner().x()) && std::isnan(mbr.max_corner().y()); } bool mbr_is_point(Box const &mbr) { return mbr.min_corner().x() == mbr.max_corner().x() && mbr.min_corner().y() == mbr.max_corner().y(); } bool mbr_is_line(Box const &mbr) { return (mbr.min_corner().x() == mbr.max_corner().x()) != (mbr.min_corner().y() == mbr.max_corner().y()); } /// Merges a vector of Cartesian MBRs into one common MBR. /// /// Since the coordinate system doesn't wrap, the order in which MBRs are /// expanded doesn't matter. /// /// @param[in] boxes Vector of MBRs to merge. /// @param[out] mbr The resulting MBR. static void merge_mbrs(const std::vector &boxes, Cartesian_box *mbr) { if (!boxes.empty()) *mbr = boxes[0]; for (auto &box : boxes) bg::expand(*mbr, box); } /// Merges a vector of geographic MBRs into one common MBR. /// /// The coordinate system wraps, so the MBRs must be expanded in the correct /// order to avoid creating an MBR that is larger than necessary. /// /// If the vector of boxes is empty, the result MBR is unchanged. /// /// @param[in] boxes Vector of MBRs to merge. /// @param[out] mbr The resulting MBR. static void merge_mbrs(const std::vector &boxes, Geographic_box *mbr) { if (!boxes.empty()) bg::detail::envelope::envelope_range_of_boxes::apply(boxes, *mbr); } /// Computes the envelope of a Cartesian geometry. /// /// The MBR returned may be a collapsed box. /// /// @param[in] g The geometry. /// @param[out] mbr The envelope of g. static void cartesian_envelope(const Geometry *g, Cartesian_box *mbr) { switch (g->type()) { case Geometry_type::kPoint: bg::envelope(*down_cast(g), *mbr); break; case Geometry_type::kLinestring: bg::envelope(*down_cast(g), *mbr); break; case Geometry_type::kPolygon: bg::envelope(*down_cast(g), *mbr); break; case Geometry_type::kGeometrycollection: { std::vector boxes; Cartesian_box geom_mbr; for (auto geom : *down_cast(g)) { switch (geom->type()) { case Geometry_type::kPoint: bg::envelope(*down_cast(geom), geom_mbr); break; case Geometry_type::kLinestring: bg::envelope(*down_cast(geom), geom_mbr); break; case Geometry_type::kPolygon: bg::envelope(*down_cast(geom), geom_mbr); break; case Geometry_type::kGeometrycollection: cartesian_envelope(geom, &geom_mbr); break; case Geometry_type::kMultipoint: bg::envelope(*down_cast(geom), geom_mbr); break; case Geometry_type::kMultilinestring: bg::envelope(*down_cast(geom), geom_mbr); break; case Geometry_type::kMultipolygon: bg::envelope(*down_cast(geom), geom_mbr); break; case Geometry_type::kGeometry: assert(false); throw std::exception(); } // Cartesian_boxxes around empty geometries contain NaN in all // coordinates. If // passed to bg::expand, the result will be a box with all NaN // coordinates. Therefore, we skip empty boxes. if (!mbr_is_empty(geom_mbr)) boxes.push_back(geom_mbr); } merge_mbrs(boxes, mbr); break; } case Geometry_type::kMultipoint: bg::envelope(*down_cast(g), *mbr); break; case Geometry_type::kMultilinestring: bg::envelope(*down_cast(g), *mbr); break; case Geometry_type::kMultipolygon: bg::envelope(*down_cast(g), *mbr); break; case Geometry_type::kGeometry: assert(false); throw std::exception(); break; } } /// Computes the envelope of a geographic geometry. /// /// The MBR returned may be a collapsed box. /// /// @param[in] g The geometry. /// @param[in] semi_major Semi-major axis of ellipsoid. /// @param[in] semi_minor Semi-minor axis of ellipsoid. /// @param[out] mbr The envelope of g. static void geographic_envelope(const Geometry *g, double semi_major, double semi_minor, Geographic_box *mbr) { bg::strategy::envelope::geographic> strategy(bg::srs::spheroid(semi_major, semi_minor)); switch (g->type()) { case Geometry_type::kPoint: bg::envelope(*down_cast(g), *mbr); break; case Geometry_type::kLinestring: bg::envelope(*down_cast(g), *mbr, strategy); break; case Geometry_type::kPolygon: bg::envelope(*down_cast(g), *mbr, strategy); break; case Geometry_type::kGeometrycollection: { std::vector boxes; Geographic_box geom_mbr; for (auto geom : *down_cast(g)) { switch (geom->type()) { case Geometry_type::kPoint: bg::envelope(*down_cast(geom), geom_mbr); break; case Geometry_type::kLinestring: bg::envelope(*down_cast(geom), geom_mbr, strategy); break; case Geometry_type::kPolygon: bg::envelope(*down_cast(geom), geom_mbr, strategy); break; case Geometry_type::kGeometrycollection: geographic_envelope(geom, semi_major, semi_minor, &geom_mbr); break; case Geometry_type::kMultipoint: bg::envelope(*down_cast(geom), geom_mbr); break; case Geometry_type::kMultilinestring: bg::envelope(*down_cast(geom), geom_mbr, strategy); break; case Geometry_type::kMultipolygon: bg::envelope(*down_cast(geom), geom_mbr, strategy); break; case Geometry_type::kGeometry: assert(false); throw std::exception(); } // Geographic_boxxes around empty geometries contain NaN in all // coordinates. If // passed to bg::expand, the result will be a box with all NaN // coordinates. Therefore, we skip empty boxes. if (!mbr_is_empty(geom_mbr)) boxes.push_back(geom_mbr); } merge_mbrs(boxes, mbr); break; } case Geometry_type::kMultipoint: bg::envelope(*down_cast(g), *mbr); break; case Geometry_type::kMultilinestring: bg::envelope(*down_cast(g), *mbr, strategy); break; case Geometry_type::kMultipolygon: bg::envelope(*down_cast(g), *mbr, strategy); break; case Geometry_type::kGeometry: assert(false); throw std::exception(); break; } } void box_envelope(const Geometry *g, const dd::Spatial_reference_system *srs, Box *mbr) { switch (g->coordinate_system()) { case Coordinate_system::kCartesian: cartesian_envelope(g, down_cast(mbr)); break; case Coordinate_system::kGeographic: geographic_envelope(g, srs ? srs->semi_major_axis() : 0.0, srs ? srs->semi_minor_axis() : 0.0, down_cast(mbr)); break; } } bool knn_query_to_mbr(THD *thd, Item *knn_query_item, double (&coordinates)[5]) { auto geometryExtractionResult = ExtractGeometry(knn_query_item, thd, nullptr); std::unique_ptr g; const dd::Spatial_reference_system *srs = nullptr; switch (geometryExtractionResult.GetResultType()) { case ResultType::Error: return true; case ResultType::NullValue: return true; case ResultType::Value: g = geometryExtractionResult.GetValue(); srs = geometryExtractionResult.GetSrs(); break; } switch (g->coordinate_system()) { case gis::Coordinate_system::kCartesian: { gis::Cartesian_box mbr; gis::box_envelope(g.get(), srs, &mbr); coordinates[0] = mbr.min_corner().x(); coordinates[1] = mbr.max_corner().x(); coordinates[2] = mbr.min_corner().y(); coordinates[3] = mbr.max_corner().y(); break; } case gis::Coordinate_system::kGeographic: { gis::Geographic_box mbr; assert(srs != nullptr); gis::box_envelope(g.get(), srs, &mbr); coordinates[0] = mbr.min_corner().x(); coordinates[1] = mbr.max_corner().x(); coordinates[2] = mbr.min_corner().y(); coordinates[3] = mbr.max_corner().y(); break; } } return false; } } // namespace gis