/* Copyright (c) 2015, 2021, 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 also distributed 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 included with MySQL.

   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 */

#include "my_config.h"
#include "item_geofunc_internal.h"

#include "item_geofunc_relchecks_bgwrap.h"

/**
  Dispatcher for 'point WITHIN xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a point.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::point_within_geometry(Geometry *g1, Geometry *g2,
                                               my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  if (gt2 == Geometry::wkb_polygon)
    BGCALL(result, within, Point, g1, Polygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipolygon)
    BGCALL(result, within, Point, g1, Multipolygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_point)
    BGCALL(result, within, Point, g1, Point, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipoint)
    BGCALL(result, within, Point, g1, Multipoint, g2, pnull_value);
  else if (gt2 == Geometry::wkb_linestring)
    BGCALL(result, within, Point, g1, Linestring, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multilinestring)
    BGCALL(result, within, Point, g1, Multilinestring, g2, pnull_value);
  else
    assert(false);

  return result;
}


/**
  Dispatcher for 'multipoint WITHIN xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multipoint.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::multipoint_within_geometry(Geometry *g1, Geometry *g2,
                                                    my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();
  const void *data_ptr= NULL;

  Multipoint mpts(g1->get_data_ptr(), g1->get_data_size(),
                  g1->get_flags(), g1->get_srid());
  if (gt2 == Geometry::wkb_polygon)
  {
    data_ptr= g2->normalize_ring_order();
    if (data_ptr == NULL)
    {
      my_error(ER_GIS_INVALID_DATA, MYF(0), "st_within");
      *pnull_value= true;
      return result;
    }

    Polygon plg(data_ptr, g2->get_data_size(),
                g2->get_flags(), g2->get_srid());

    result= multipoint_within_geometry_internal(mpts, plg);
  }
  else if (gt2 == Geometry::wkb_multipolygon)
  {
    data_ptr= g2->normalize_ring_order();
    if (data_ptr == NULL)
    {
      *pnull_value= true;
      my_error(ER_GIS_INVALID_DATA, MYF(0), "st_within");
      return result;
    }

    Multipolygon mplg(data_ptr, g2->get_data_size(),
                      g2->get_flags(), g2->get_srid());

    /*
      One may want to build the rtree index on mpts when mpts has more
      components than mplg, but then one would have to track the points that
      are already known to be in one of mplg's polygons and avoid checking
      again (which may fail and cause false alarm) for other polygon components.
      Such maintenance brings extra cost and performance test prooves that
      it's not desirable.
      The containers tried for such maintenance including std::vector<bool>,
      std::set<array_index>, mpts[i].set_props().

      Also, even if the mplg has only one polygon, i.e. the worst case for
      building rtree index on mplg, the performance is still very very close to
      the linear search done in multipoint_within_geometry_internal.

      So always build index on mplg as below.
    */
    result= multipoint_within_multipolygon(mpts, mplg);
  }
  else if (gt2 == Geometry::wkb_point)
  {
    /* There may be duplicate Points, thus use a set to make them unique*/
    Point_set ptset1(mpts.begin(), mpts.end());
    Point pt(g2->get_data_ptr(),
             g2->get_data_size(), g2->get_flags(), g2->get_srid());
    result= ((ptset1.size() == 1) &&
             boost::geometry::equals(*ptset1.begin(), pt));
  }
  else if (gt2 == Geometry::wkb_multipoint)
  {
    /* There may be duplicate Points, thus use a set to make them unique*/
    Point_set ptset1(mpts.begin(), mpts.end());
    Multipoint mpts2(g2->get_data_ptr(),
                     g2->get_data_size(), g2->get_flags(), g2->get_srid());
    Point_set ptset2(mpts2.begin(), mpts2.end());
    Point_vector respts;
    TYPENAME Point_vector::iterator endpos;
    respts.resize(std::max(ptset1.size(), ptset2.size()));
    endpos= std::set_intersection(ptset1.begin(), ptset1.end(),
                                  ptset2.begin(), ptset2.end(),
                                  respts.begin(), bgpt_lt());
    result= (ptset1.size() == static_cast<size_t>(endpos - respts.begin()));
  }
  else if (gt2 == Geometry::wkb_linestring)
  {
    Linestring ls(g2->get_data_ptr(), g2->get_data_size(),
                  g2->get_flags(), g2->get_srid());
    result= multipoint_within_geometry_internal(mpts, ls);
  }
  else if (gt2 == Geometry::wkb_multilinestring)
  {
    Multilinestring mls(g2->get_data_ptr(), g2->get_data_size(),
                        g2->get_flags(), g2->get_srid());
    /*
      Here we can't separate linestrings of a multilinstring MLS to do within
      check one by one because if N (N > 1) linestrings share the same boundary
      point P, P may or may not be a boundary point of MLS, depending on N%2,
      if N is an even number P is an internal point of MLS, otherwise P is a
      boundary point of MLS.
    */
    result= multipoint_within_geometry_internal(mpts, mls);
  }
  else
    assert(false);

  return result;
}


template <typename Geom_types>
template <typename GeomType>
int BG_wrap<Geom_types>::
multipoint_within_geometry_internal(const Multipoint &mpts,
                                    const GeomType &geom)
{
  bool has_inner= false;

  for (TYPENAME Multipoint::iterator i= mpts.begin(); i != mpts.end(); ++i)
  {
    /*
      Checking for intersects is faster than within, so if there is at least
      one point within geom, only check that the rest points intersects geom.
     */
    if (!has_inner && (has_inner= boost::geometry::within(*i, geom)))
      continue;

    if (!boost::geometry::intersects(*i, geom))
      return 0;
  }

  return has_inner;
}


template <typename Geom_types>
int BG_wrap<Geom_types>::
multipoint_within_multipolygon(const Multipoint &mpts,
                               const Multipolygon &mplgn)
{
  bool has_inner= false;

  Rtree_index rtree;
  make_rtree_bggeom(mplgn, &rtree);
  BG_box box;

  for (TYPENAME Multipoint::iterator i= mpts.begin(); i != mpts.end(); ++i)
  {
    bool already_in= false;
    // Search for polygons that may intersect *i point using the rtree index.
    boost::geometry::envelope(*i, box);
    Rtree_index::const_query_iterator j= rtree.qbegin(bgi::intersects(box));
    if (j == rtree.qend())
      return 0;
    /*
      All polygons that possibly intersect *i point are given by the
      rtree iteration below.
    */
    for (; j != rtree.qend(); ++j)
    {
      /*
        Checking for intersects is faster than within, so if there is at least
        one point within geom, only check that the rest points intersects geom.
      */
      const Polygon &plgn= mplgn[j->second];
      /*
        If we don't have a point in mpts that's within mplgn yet,
        check whether *i is within plgn.
        If *i is within plgn, it's already in the multipolygon, so no need
        for more checks.
      */
      if (!has_inner && (has_inner= boost::geometry::within(*i, plgn)))
      {
        already_in= true;
        break;
      }

      /*
        If we already have a point within mplgn, OR if *i is checked above to
        be not within plgn, check whether *i intersects plgn.
        *i has to intersect one of the components in this loop, otherwise *i
        is out of mplgn.
       */
      if (boost::geometry::intersects(*i, plgn))
      {
        already_in= true;
        /*
          It's likely that *i is within another plgn, so only stop the
          iteration if we already have a point that's within the multipolygon,
          in order not to miss the polygon containing *i.
        */
        if (has_inner)
          break;
      }
    }

    /*
      The *i point isn't within or intersects any polygon of mplgn,
      so mpts isn't within geom.
    */
    if (!already_in)
      return 0;
  }

  /*
    All points in mpts at least intersects geom, so the result is determined
    by whether there is at least one point in mpts that's within geom.
  */
  return has_inner;
}


template<typename Geom_types>
int BG_wrap<Geom_types>::
linestring_within_geometry(Geometry *g1, Geometry *g2,
                           my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  if (gt2 == Geometry::wkb_polygon)
    BGCALL(result, within, Linestring, g1, Polygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipolygon)
    BGCALL(result, within, Linestring, g1, Multipolygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_point || gt2 == Geometry::wkb_multipoint)
    return 0;
  else if (gt2 == Geometry::wkb_linestring)
    BGCALL(result, within, Linestring, g1, Linestring, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multilinestring)
    BGCALL(result, within, Linestring, g1, Multilinestring, g2, pnull_value);
  else
    assert(false);

  return result;
}


template<typename Geom_types>
int BG_wrap<Geom_types>::
multilinestring_within_geometry(Geometry *g1, Geometry *g2,
                                my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  if (gt2 == Geometry::wkb_polygon)
    BGCALL(result, within, Multilinestring, g1, Polygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipolygon)
    BGCALL(result, within, Multilinestring, g1, Multipolygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_point || gt2 == Geometry::wkb_multipoint)
    return 0;
  else if (gt2 == Geometry::wkb_linestring)
    BGCALL(result, within, Multilinestring, g1,
           Linestring, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multilinestring)
    BGCALL(result, within, Multilinestring, g1,
           Multilinestring, g2, pnull_value);
  else
    assert(false);

  return result;
}


template<typename Geom_types>
int BG_wrap<Geom_types>::
polygon_within_geometry(Geometry *g1, Geometry *g2,
                        my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  if (gt2 == Geometry::wkb_polygon)
    BGCALL(result, within, Polygon, g1, Polygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipolygon)
    BGCALL(result, within, Polygon, g1, Multipolygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_point || gt2 == Geometry::wkb_multipoint ||
           gt2 == Geometry::wkb_linestring ||
           gt2 == Geometry::wkb_multilinestring)
    return 0;
  else
    assert(false);

  return result;
}


template<typename Geom_types>
int BG_wrap<Geom_types>::
multipolygon_within_geometry(Geometry *g1, Geometry *g2,
                             my_bool *pnull_value)
{

  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  if (gt2 == Geometry::wkb_polygon)
    BGCALL(result, within, Multipolygon, g1, Polygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipolygon)
    BGCALL(result, within, Multipolygon, g1, Multipolygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_point || gt2 == Geometry::wkb_multipoint ||
           gt2 == Geometry::wkb_linestring ||
           gt2 == Geometry::wkb_multilinestring)
    return 0;
  else
    assert(false);

  return result;
}


/**
  Dispatcher for 'multipoint EQUALS xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multipoint.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::multipoint_equals_geometry(Geometry *g1, Geometry *g2,
                                                    my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_point:
    result= Ifsr::equals_check<Geom_types>(g2, g1, pnull_value);
    break;
  case Geometry::wkb_multipoint:
    {
      Multipoint mpts1(g1->get_data_ptr(),
                       g1->get_data_size(), g1->get_flags(), g1->get_srid());
      Multipoint mpts2(g2->get_data_ptr(),
                       g2->get_data_size(), g2->get_flags(), g2->get_srid());

      Point_set ptset1(mpts1.begin(), mpts1.end());
      Point_set ptset2(mpts2.begin(), mpts2.end());
      result= (ptset1.size() == ptset2.size() &&
               std::equal(ptset1.begin(), ptset1.end(),
                          ptset2.begin(), bgpt_eq()));
    }
    break;
  default:
    result= 0;
    break;
  }
  return result;
}


/**
  Dispatcher for 'multipoint disjoint xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multipoint.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multipoint_disjoint_geometry(Geometry *g1, Geometry *g2,
                             my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();
  const void *data_ptr= NULL;

  Multipoint mpts1(g1->get_data_ptr(),
                   g1->get_data_size(), g1->get_flags(), g1->get_srid());
  switch (gt2)
  {
  case Geometry::wkb_point:
    result= point_disjoint_geometry(g2, g1, pnull_value);
    break;
  case Geometry::wkb_multipoint:
    {
      Multipoint mpts2(g2->get_data_ptr(),
                       g2->get_data_size(), g2->get_flags(), g2->get_srid());
      Point_set ptset1(mpts1.begin(), mpts1.end());
      Point_set ptset2(mpts2.begin(), mpts2.end());
      Point_vector respts;
      TYPENAME Point_vector::iterator endpos;
      size_t ptset1sz= ptset1.size(), ptset2sz= ptset2.size();

      respts.resize(ptset1sz > ptset2sz ? ptset1sz : ptset2sz);
      endpos= std::set_intersection(ptset1.begin(), ptset1.end(),
                                    ptset2.begin(), ptset2.end(),
                                    respts.begin(), bgpt_lt());
      result= (endpos == respts.begin());
    }
    break;
  case Geometry::wkb_polygon:
    {
      data_ptr= g2->normalize_ring_order();
      if (data_ptr == NULL)
      {
        *pnull_value= true;
        my_error(ER_GIS_INVALID_DATA, MYF(0), "st_disjoint");
        return result;
      }

      Polygon plg(data_ptr, g2->get_data_size(),
                  g2->get_flags(), g2->get_srid());
      result= multipoint_disjoint_geometry_internal(mpts1, plg);
    }
    break;
  case Geometry::wkb_multipolygon:
    {
      data_ptr= g2->normalize_ring_order();
      if (data_ptr == NULL)
      {
        *pnull_value= true;
        my_error(ER_GIS_INVALID_DATA, MYF(0), "st_disjoint");
        return result;
      }

      Multipolygon mplg(data_ptr, g2->get_data_size(),
                        g2->get_flags(), g2->get_srid());
      result= multipoint_disjoint_multi_geometry(mpts1, mplg);
    }
    break;
  case Geometry::wkb_linestring:
    {
      Linestring ls(g2->get_data_ptr(), g2->get_data_size(),
                    g2->get_flags(), g2->get_srid());
      result= multipoint_disjoint_geometry_internal(mpts1, ls);
    }
    break;
  case Geometry::wkb_multilinestring:
    {
      Multilinestring mls(g2->get_data_ptr(), g2->get_data_size(),
                          g2->get_flags(), g2->get_srid());
      result= multipoint_disjoint_multi_geometry(mpts1, mls);
    }
    break;
  default:
    assert(false);
    break;
  }

  return result;
}


template<typename Geom_types>
template<typename Geom_type>
int BG_wrap<Geom_types>::
multipoint_disjoint_geometry_internal(const Multipoint &mpts,
                                      const Geom_type &geom)
{
  for (TYPENAME Multipoint::iterator i= mpts.begin(); i != mpts.end(); ++i)
  {
    if (!boost::geometry::disjoint(*i, geom))
      return 0;
  }

  return 1;
}


template<typename Geom_types>
template<typename Geom_type>
int BG_wrap<Geom_types>::
multipoint_disjoint_multi_geometry(const Multipoint &mpts,
                                   const Geom_type &geom)
{
  Rtree_index rtree;

  // Choose the one with more components to build rtree index on, to get more
  // performance improvement.
  if (mpts.size() > geom.size())
  {
    make_rtree_bggeom(mpts, &rtree);
    for (TYPENAME Geom_type::iterator j= geom.begin(); j != geom.end(); ++j)
    {
      BG_box box;
      boost::geometry::envelope(*j, box);

      /*
        For each component *j in geom, find points in mpts who intersect
        with MBR(*j), such points are likely to intersect *j, the rest are
        for sure disjoint *j thus no need to check precisely.
      */
      for (Rtree_index::const_query_iterator
           i= rtree.qbegin(bgi::intersects(box));
           i != rtree.qend(); ++i)
      {
        /*
          If *i really intersect *j, we have the result as false;
          If no *i intersects *j, *j disjoint mpts.
          And if no *j intersect mpts, we can conclude that mpts disjoint geom.
        */
        if (!boost::geometry::disjoint(mpts[i->second], *j))
          return 0;
      }
    }
  }
  else
  {
    make_rtree_bggeom(geom, &rtree);
    for (TYPENAME Multipoint::iterator j= mpts.begin(); j != mpts.end(); ++j)
    {
      BG_box box;
      boost::geometry::envelope(*j, box);

      /*
        For each point *j in mpts, find components *i in geom such that
        MBR(*i) intersect *j, such *i are likely to intersect *j, the rest are
        for sure disjoint *j thus no need to check precisely.
      */
      for (Rtree_index::const_query_iterator
           i= rtree.qbegin(bgi::intersects(box));
           i != rtree.qend(); ++i)
      {
        /*
          If *i really intersect *j, we have the result as false;
          If no *i intersects *j, *j disjoint geom.
          And if no *j intersect geom, we can conclude that mpts disjoint geom.
        */
        if (!boost::geometry::disjoint(geom[i->second], *j))
          return 0;
      }
    }
  }

  return 1;
}


/**
  Dispatcher for 'linestring disjoint xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a linestring.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
linestring_disjoint_geometry(Geometry *g1, Geometry *g2,
                             my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  if (gt2 == Geometry::wkb_linestring)
    BGCALL(result, disjoint, Linestring, g1, Linestring, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multilinestring)
    BGCALL(result, disjoint, Linestring, g1, Multilinestring, g2, pnull_value);
  else if (gt2 == Geometry::wkb_point)
    BGCALL(result, disjoint, Linestring, g1, Point, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipoint)
    result= multipoint_disjoint_geometry(g2, g1, pnull_value);
  else if (gt2 == Geometry::wkb_polygon)
    BGCALL(result, disjoint, Linestring, g1, Polygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipolygon)
    BGCALL(result, disjoint, Linestring, g1, Multipolygon, g2, pnull_value);
  else
    assert(false);
  return result;
}


/**
  Dispatcher for 'multilinestring disjoint xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multilinestring.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multilinestring_disjoint_geometry(Geometry *g1, Geometry *g2,
                                  my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  if (gt2 == Geometry::wkb_linestring)
    result= BG_wrap<Geom_types>::
      linestring_disjoint_geometry(g2, g1, pnull_value);
  else if (gt2 == Geometry::wkb_multilinestring)
    BGCALL(result, disjoint, Multilinestring, g1, Multilinestring, g2, pnull_value);
  else if (gt2 == Geometry::wkb_point)
    BGCALL(result, disjoint, Multilinestring, g1, Point, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipoint)
    result= multipoint_disjoint_geometry(g2, g1, pnull_value);
  else if (gt2 == Geometry::wkb_polygon)
    BGCALL(result, disjoint, Multilinestring, g1, Polygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipolygon)
    BGCALL(result, disjoint, Multilinestring, g1, Multipolygon, g2, pnull_value);
  else
    assert(false);

  return result;
}


/**
  Dispatcher for 'point disjoint xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a point.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
point_disjoint_geometry(Geometry *g1, Geometry *g2,
                        my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_point:
    BGCALL(result, disjoint, Point, g1, Point, g2, pnull_value);
    break;
  case Geometry::wkb_polygon:
    BGCALL(result, disjoint, Point, g1, Polygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, disjoint, Point, g1, Multipolygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipoint:
    {
      Multipoint mpts(g2->get_data_ptr(),
                      g2->get_data_size(), g2->get_flags(), g2->get_srid());
      Point pt(g1->get_data_ptr(),
               g1->get_data_size(), g1->get_flags(), g1->get_srid());

      Point_set ptset(mpts.begin(), mpts.end());
      result= (ptset.find(pt) == ptset.end());
    }
    break;
  case Geometry::wkb_linestring:
    BGCALL(result, disjoint, Point, g1, Linestring, g2, pnull_value);
    break;
  case Geometry::wkb_multilinestring:
    BGCALL(result, disjoint, Point, g1, Multilinestring, g2, pnull_value);
    break;
  default:
    assert(false);
    break;
  }
  return result;
}


/**
  Dispatcher for 'polygon disjoint xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a polygon.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
polygon_disjoint_geometry(Geometry *g1, Geometry *g2,
                          my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_point:
    BGCALL(result, disjoint, Polygon, g1, Point, g2, pnull_value);
    break;
  case Geometry::wkb_multipoint:
    result= multipoint_disjoint_geometry(g2, g1, pnull_value);
    break;
  case Geometry::wkb_polygon:
    BGCALL(result, disjoint, Polygon, g1, Polygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, disjoint, Polygon, g1, Multipolygon, g2, pnull_value);
    break;
  case Geometry::wkb_linestring:
    BGCALL(result, disjoint, Polygon, g1, Linestring, g2, pnull_value);
    break;
  case Geometry::wkb_multilinestring:
    BGCALL(result, disjoint, Polygon, g1, Multilinestring, g2, pnull_value);
    break;
  default:
    assert(false);
    break;
  }
  return result;
}


/**
  Dispatcher for 'multipolygon disjoint xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multipolygon.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multipolygon_disjoint_geometry(Geometry *g1, Geometry *g2,
                               my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_point:
    BGCALL(result, disjoint, Multipolygon, g1, Point, g2, pnull_value);
    break;
  case Geometry::wkb_multipoint:
    result= multipoint_disjoint_geometry(g2, g1, pnull_value);
    break;
  case Geometry::wkb_polygon:
    BGCALL(result, disjoint, Multipolygon, g1, Polygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, disjoint, Multipolygon, g1, Multipolygon, g2, pnull_value);
    break;
  case Geometry::wkb_linestring:
    BGCALL(result, disjoint, Multipolygon, g1, Linestring, g2, pnull_value);
    break;
  case Geometry::wkb_multilinestring:
    BGCALL(result, disjoint, Multipolygon, g1, Multilinestring, g2, pnull_value);
    break;
  default:
    assert(false);
    break;
  }

  return result;
}


/**
  Dispatcher for 'point intersects xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a point.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
point_intersects_geometry(Geometry *g1, Geometry *g2,
                          my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_point:
    BGCALL(result, intersects, Point, g1, Point, g2, pnull_value);
    break;
  case Geometry::wkb_multipoint:
  case Geometry::wkb_linestring:
  case Geometry::wkb_multilinestring:
    result= !point_disjoint_geometry(g1, g2, pnull_value);
    break;
  case Geometry::wkb_polygon:
    BGCALL(result, intersects, Point, g1, Polygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, intersects, Point, g1, Multipolygon, g2, pnull_value);
    break;
  default:
    assert(false);
    break;
  }
  return result;
}


/**
  Dispatcher for 'multipoint intersects xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multipoint.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multipoint_intersects_geometry(Geometry *g1, Geometry *g2,
                               my_bool *pnull_value)
{
  return !multipoint_disjoint_geometry(g1, g2, pnull_value);
}


/**
  Dispatcher for 'linestring intersects xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a linestring.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
linestring_intersects_geometry(Geometry *g1, Geometry *g2,
                               my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  if (gt2 == Geometry::wkb_point)
    BGCALL(result, intersects, Linestring, g1, Point, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipoint)
    result= multipoint_intersects_geometry(g2, g1, pnull_value);
  else if (gt2 == Geometry::wkb_linestring)
    BGCALL(result, intersects, Linestring, g1, Linestring, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multilinestring)
    BGCALL(result, intersects, Linestring, g1, Multilinestring, g2, pnull_value);
  else if (gt2 == Geometry::wkb_polygon)
    BGCALL(result, intersects, Linestring, g1, Polygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipolygon)
    BGCALL(result, intersects, Linestring, g1, Multipolygon, g2, pnull_value);
  else
    assert(false);

  return result;
}


/**
  Dispatcher for 'multilinestring intersects xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multilinestring.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multilinestring_intersects_geometry(Geometry *g1, Geometry *g2,
                                    my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  if (gt2 == Geometry::wkb_point)
    BGCALL(result, intersects, Multilinestring, g1, Point, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipoint)
    result= multipoint_intersects_geometry(g2, g1, pnull_value);
  else if (gt2 == Geometry::wkb_linestring)
    BGCALL(result, intersects, Multilinestring, g1, Linestring, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multilinestring)
    BGCALL(result, intersects, Multilinestring, g1, Multilinestring, g2, pnull_value);
  else if (gt2 == Geometry::wkb_polygon)
    BGCALL(result, intersects, Multilinestring, g1, Polygon, g2, pnull_value);
  else if (gt2 == Geometry::wkb_multipolygon)
    BGCALL(result, intersects, Multilinestring, g1, Multipolygon, g2, pnull_value);
  else
    assert(false);

  return result;
}


/**
  Dispatcher for 'polygon intersects xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a polygon.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
polygon_intersects_geometry(Geometry *g1, Geometry *g2,
                            my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_point:
    BGCALL(result, intersects, Polygon, g1, Point, g2, pnull_value);
    break;
  case Geometry::wkb_multipoint:
    result= !multipoint_disjoint_geometry(g2, g1, pnull_value);
    break;
  case Geometry::wkb_polygon:
    BGCALL(result, intersects, Polygon, g1, Polygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, intersects, Polygon, g1, Multipolygon, g2, pnull_value);
    break;
  case Geometry::wkb_linestring:
    BGCALL(result, intersects, Polygon, g1, Linestring, g2, pnull_value);
    break;
  case Geometry::wkb_multilinestring:
    BGCALL(result, intersects, Polygon, g1, Multilinestring, g2, pnull_value);
    break;
  default:
    assert(false);
    break;
  }

  return result;
}


/**
  Dispatcher for 'multipolygon intersects xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multipolygon.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multipolygon_intersects_geometry(Geometry *g1, Geometry *g2,
                                 my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_point:
    BGCALL(result, intersects, Multipolygon, g1, Point, g2, pnull_value);
    break;
  case Geometry::wkb_multipoint:
    result= !multipoint_disjoint_geometry(g2, g1, pnull_value);
    break;
  case Geometry::wkb_polygon:
    BGCALL(result, intersects, Multipolygon, g1, Polygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, intersects, Multipolygon, g1, Multipolygon, g2, pnull_value);
    break;
  case Geometry::wkb_linestring:
    BGCALL(result, intersects, Multipolygon, g1, Linestring, g2, pnull_value);
    break;
  case Geometry::wkb_multilinestring:
    BGCALL(result, intersects, Multipolygon, g1, Multilinestring, g2, pnull_value);
    break;
  default:
    assert(false);
    break;
  }
  return result;
}


/**
  Dispatcher for 'linestring crosses xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a linestring.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
linestring_crosses_geometry(Geometry *g1, Geometry *g2,
                            my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_linestring:
    BGCALL(result, crosses, Linestring, g1, Linestring, g2, pnull_value);
    break;
  case Geometry::wkb_multilinestring:
    BGCALL(result, crosses, Linestring, g1,
           Multilinestring, g2, pnull_value);
    break;
  case Geometry::wkb_polygon:
    BGCALL(result, crosses, Linestring, g1, Polygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, crosses, Linestring, g1, Multipolygon, g2, pnull_value);
    break;
  default:
    assert(false);
    break;
  }

  return result;
}


/**
  Dispatcher for 'multilinestring crosses xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multilinestring.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multilinestring_crosses_geometry(Geometry *g1, Geometry *g2,
                                 my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_linestring:
    BGCALL(result, crosses, Multilinestring, g1, Linestring, g2, pnull_value);
    break;
  case Geometry::wkb_multilinestring:
    BGCALL(result, crosses, Multilinestring, g1,
           Multilinestring, g2, pnull_value);
    break;
  case Geometry::wkb_polygon:
    BGCALL(result, crosses, Multilinestring, g1, Polygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, crosses, Multilinestring, g1, Multipolygon, g2, pnull_value);
    break;
  default:
    assert(false);
    break;
  }
  return result;
}


/**
  Dispatcher for 'multipoint crosses xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multipoint.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multipoint_crosses_geometry(Geometry *g1, Geometry *g2,
                            my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_linestring:
  case Geometry::wkb_multilinestring:
  case Geometry::wkb_polygon:
  case Geometry::wkb_multipolygon:
    {
      bool has_in= false, has_out= false;
      int res= 0;

      Multipoint mpts(g1->get_data_ptr(),
                      g1->get_data_size(), g1->get_flags(), g1->get_srid());
      /*
        According to OGC's definition to crosses, if some Points of
        g1 is in g2 and some are not, g1 crosses g2, otherwise not.
       */
      for (TYPENAME Multipoint::iterator i= mpts.begin(); i != mpts.end() &&
           !(has_in && has_out); ++i)
      {
        if (!has_out)
        {
          res= point_disjoint_geometry(&(*i), g2, pnull_value);

          if (!*pnull_value)
          {
            has_out= res;
            if (has_out)
              continue;
          }
          else
            return 0;
        }

        if (!has_in)
        {
          res= point_within_geometry(&(*i), g2, pnull_value);
          if (!*pnull_value)
            has_in= res;
          else
            return 0;
        }
      }

      result= (has_in && has_out);
    }
    break;
  default:
    assert(false);
    break;
  }

  return result;
}


/**
  Dispatcher for 'multipoint crosses xxx'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multipoint.
  @param g2 Second Geometry operand, not a geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multipoint_overlaps_multipoint(Geometry *g1, Geometry *g2,
                               my_bool *pnull_value)
{
  int result= 0;

  Multipoint mpts1(g1->get_data_ptr(),
                   g1->get_data_size(), g1->get_flags(), g1->get_srid());
  Multipoint mpts2(g2->get_data_ptr(),
                   g2->get_data_size(), g2->get_flags(), g2->get_srid());
  Point_set ptset1, ptset2;

  ptset1.insert(mpts1.begin(), mpts1.end());
  ptset2.insert(mpts2.begin(), mpts2.end());

  // They overlap if they intersect and also each has some points that the other
  // one doesn't have.
  Point_vector respts;
  TYPENAME Point_vector::iterator endpos;
  size_t ptset1sz= ptset1.size(), ptset2sz= ptset2.size(), resptssz;

  respts.resize(ptset1sz > ptset2sz ? ptset1sz : ptset2sz);
  endpos= std::set_intersection(ptset1.begin(), ptset1.end(),
                                ptset2.begin(), ptset2.end(),
                                respts.begin(), bgpt_lt());
  resptssz= endpos - respts.begin();
  if (resptssz > 0 && resptssz < ptset1.size() &&
      resptssz < ptset2.size())
    result= 1;
  else
    result= 0;

  return result;
}


/**
  Dispatcher for 'multilinestring touches polygon'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multilinestring.
  @param g2 Second Geometry operand, a polygon.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multilinestring_touches_polygon(Geometry *g1, Geometry *g2,
                                my_bool *pnull_value)
{

  const void *data_ptr= g2->normalize_ring_order();
  if (data_ptr == NULL)
  {
    *pnull_value= true;
    my_error(ER_GIS_INVALID_DATA, MYF(0), "st_touches");
    return 0;
  }

  Polygon plgn(data_ptr, g2->get_data_size(),
               g2->get_flags(), g2->get_srid());
  Multilinestring mls(g1->get_data_ptr(), g1->get_data_size(),
                      g1->get_flags(), g1->get_srid());

  Multipolygon mplgn;
  mplgn.push_back(plgn);

  int result= boost::geometry::touches(mls, mplgn);

  return result;
}


/**
  Dispatcher for 'point touches geometry'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a point.
  @param g2 Second Geometry operand, a geometry other than geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
point_touches_geometry(Geometry *g1, Geometry *g2,
                       my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_linestring:
    BGCALL(result, touches, Point, g1, Linestring, g2, pnull_value);
    break;
  case Geometry::wkb_multilinestring:
    BGCALL(result, touches, Point, g1, Multilinestring, g2, pnull_value);
    break;
  case Geometry::wkb_polygon:
    BGCALL(result, touches, Point, g1, Polygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, touches, Point, g1, Multipolygon, g2, pnull_value);
    break;
  default:
    assert(false);
    break;
  }

  return result;
}


/**
  Dispatcher for 'multipoint touches geometry'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multipoint.
  @param g2 Second Geometry operand, a geometry other than geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multipoint_touches_geometry(Geometry *g1, Geometry *g2,
                            my_bool *pnull_value)
{
  int has_touches= 0;

  Multipoint mpts(g1->get_data_ptr(), g1->get_data_size(),
                  g1->get_flags(), g1->get_srid());
  for (TYPENAME Multipoint::iterator i= mpts.begin(); i != mpts.end(); ++i)
  {
    int ptg= point_touches_geometry(&(*i), g2, pnull_value);
    if (*pnull_value)
      return 0;
    if (ptg)
      has_touches= 1;
    else if (!point_disjoint_geometry(&(*i), g2, pnull_value))
      return 0;
  }

  return has_touches;
}


/**
  Dispatcher for 'linestring touches geometry'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a linestring.
  @param g2 Second Geometry operand, a geometry other than geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
linestring_touches_geometry(Geometry *g1, Geometry *g2,
                            my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_point:
    BGCALL(result, touches, Linestring, g1, Point, g2, pnull_value);
    break;
  case Geometry::wkb_multipoint:
    result= multipoint_touches_geometry(g2, g1, pnull_value);
    break;
  case Geometry::wkb_linestring:
    BGCALL(result, touches, Linestring, g1, Linestring, g2, pnull_value);
    break;
  case Geometry::wkb_multilinestring:
    BGCALL(result, touches, Linestring, g1, Multilinestring, g2, pnull_value);
    break;
  case Geometry::wkb_polygon:
    BGCALL(result, touches, Linestring, g1, Polygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, touches, Linestring, g1, Multipolygon, g2, pnull_value);
    break;
  default:
    assert(false);
    break;
  }
  return result;
}


/**
  Dispatcher for 'multilinestring touches geometry'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multilinestring.
  @param g2 Second Geometry operand, a geometry other than geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multilinestring_touches_geometry(Geometry *g1, Geometry *g2,
                                 my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_point:
    BGCALL(result, touches, Multilinestring, g1, Point, g2, pnull_value);
    break;
  case Geometry::wkb_multipoint:
    result= multipoint_touches_geometry(g2, g1, pnull_value);
    break;
  case Geometry::wkb_linestring:
    BGCALL(result, touches, Multilinestring, g1, Linestring, g2, pnull_value);
    break;
  case Geometry::wkb_multilinestring:
    BGCALL(result, touches, Multilinestring, g1, Multilinestring, g2, pnull_value);
    break;
  case Geometry::wkb_polygon:
    result= BG_wrap<Geom_types>::
      multilinestring_touches_polygon(g1, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, touches, Multilinestring, g1, Multipolygon, g2, pnull_value);
    break;
  default:
    assert(false);
    break;
  }
  return result;
}


/**
  Dispatcher for 'polygon touches geometry'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a polygon.
  @param g2 Second Geometry operand, a geometry other than geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
polygon_touches_geometry(Geometry *g1, Geometry *g2,
                         my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_point:
    BGCALL(result, touches, Polygon, g1, Point, g2, pnull_value);
    break;
  case Geometry::wkb_multipoint:
    result= multipoint_touches_geometry(g2, g1, pnull_value);
    break;
  case Geometry::wkb_polygon:
    BGCALL(result, touches, Polygon, g1, Polygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, touches, Polygon, g1, Multipolygon, g2, pnull_value);
    break;
  case Geometry::wkb_linestring:
    BGCALL(result, touches, Polygon, g1, Linestring, g2, pnull_value);
    break;
  case Geometry::wkb_multilinestring:
    result= BG_wrap<Geom_types>::
      multilinestring_touches_polygon(g2, g1, pnull_value);
    break;
  default:
    assert(false);
    break;
  }
  return result;
}


/**
  Dispatcher for 'multipolygon touches geometry'.

  @tparam Geom_types Geometry types definitions.
  @param g1 First Geometry operand, a multipolygon.
  @param g2 Second Geometry operand, a geometry other than geometry collection.
  @param[out] pnull_value Returns whether error occured duirng the computation.
  @return 0 if specified relation doesn't hold for the given operands,
                otherwise returns none 0.
 */
template<typename Geom_types>
int BG_wrap<Geom_types>::
multipolygon_touches_geometry(Geometry *g1, Geometry *g2,
                              my_bool *pnull_value)
{
  int result= 0;
  Geometry::wkbType gt2= g2->get_type();

  switch (gt2)
  {
  case Geometry::wkb_point:
    BGCALL(result, touches, Multipolygon, g1, Point, g2, pnull_value);
    break;
  case Geometry::wkb_multipoint:
    result= multipoint_touches_geometry(g2, g1, pnull_value);
    break;
  case Geometry::wkb_polygon:
    BGCALL(result, touches, Multipolygon, g1, Polygon, g2, pnull_value);
    break;
  case Geometry::wkb_multipolygon:
    BGCALL(result, touches, Multipolygon, g1, Multipolygon, g2, pnull_value);
    break;
  case Geometry::wkb_linestring:
    BGCALL(result, touches, Multipolygon, g1, Linestring, g2, pnull_value);
    break;
  case Geometry::wkb_multilinestring:
    BGCALL(result, touches, Multipolygon, g1, Multilinestring, g2, pnull_value);
    break;
  default:
    assert(false);
    break;
  }

  return result;
}

/*
  Explicit template instantiations are needed here.
  Templates were moved to separate file in order to avoid
  inlining and out-of-memory problems in optmized mode on gcc/solaris/intel.
 */
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
point_within_geometry(Geometry *g1, Geometry *g2,
                      my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multipoint_within_geometry(Geometry *g1, Geometry *g2,
                           my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
linestring_within_geometry(Geometry *g1, Geometry *g2,
                           my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multilinestring_within_geometry(Geometry *g1, Geometry *g2,
                                my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
polygon_within_geometry(Geometry *g1, Geometry *g2,
                        my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multipolygon_within_geometry(Geometry *g1, Geometry *g2,
                             my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multipoint_equals_geometry(Geometry *g1, Geometry *g2, my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
point_disjoint_geometry(Geometry *g1, Geometry *g2,
                        my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multipoint_disjoint_geometry(Geometry *g1, Geometry *g2,
                             my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
linestring_disjoint_geometry(Geometry *g1, Geometry *g2,
                             my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multilinestring_disjoint_geometry(Geometry *g1, Geometry *g2,
                                  my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
polygon_disjoint_geometry(Geometry *g1, Geometry *g2,
                          my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multipolygon_disjoint_geometry(Geometry *g1, Geometry *g2,
                               my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
point_intersects_geometry(Geometry *g1, Geometry *g2,
                          my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multipoint_intersects_geometry(Geometry *g1, Geometry *g2,
                               my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
linestring_intersects_geometry(Geometry *g1, Geometry *g2,
                               my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multilinestring_intersects_geometry(Geometry *g1, Geometry *g2,
                                    my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
polygon_intersects_geometry(Geometry *g1, Geometry *g2,
                            my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multipolygon_intersects_geometry(Geometry *g1, Geometry *g2,
                                 my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
linestring_crosses_geometry(Geometry *g1, Geometry *g2,
                            my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multipoint_crosses_geometry(Geometry *g1, Geometry *g2,
                            my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multilinestring_crosses_geometry(Geometry *g1, Geometry *g2,
                                 my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multipoint_overlaps_multipoint(Geometry *g1, Geometry *g2,
                               my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
point_touches_geometry(Geometry *g1, Geometry *g2,
                       my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multipoint_touches_geometry(Geometry *g1, Geometry *g2,
                            my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
linestring_touches_geometry(Geometry *g1, Geometry *g2,
                            my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multilinestring_touches_polygon(Geometry *g1, Geometry *g2,
                                my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multilinestring_touches_geometry(Geometry *g1, Geometry *g2,
                                 my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
polygon_touches_geometry(Geometry *g1, Geometry *g2,
                         my_bool *pnull_value);
template
int BG_wrap<BG_models<boost::geometry::cs::cartesian> > ::
multipolygon_touches_geometry(Geometry *g1, Geometry *g2,
                              my_bool *pnull_value);
