summaryrefslogtreecommitdiff
path: root/sql/item_geofunc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item_geofunc.cc')
-rw-r--r--sql/item_geofunc.cc651
1 files changed, 648 insertions, 3 deletions
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index d9200b3e8d3..d38729771f8 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -218,6 +218,130 @@ String *Item_func_envelope::val_str(String *str)
}
+int Item_func_boundary::Transporter::single_point(double x, double y)
+{
+ return 0;
+}
+
+
+int Item_func_boundary::Transporter::start_line()
+{
+ n_points= 0;
+ current_type= Gcalc_function::shape_line;
+ return 0;
+}
+
+
+int Item_func_boundary::Transporter::complete_line()
+{
+ current_type= (Gcalc_function::shape_type) 0;
+ if (n_points > 1)
+ return m_receiver->single_point(last_x, last_y);
+ return 0;
+}
+
+
+int Item_func_boundary::Transporter::start_poly()
+{
+ current_type= Gcalc_function::shape_polygon;
+ return 0;
+}
+
+
+int Item_func_boundary::Transporter::complete_poly()
+{
+ current_type= (Gcalc_function::shape_type) 0;
+ return 0;
+}
+
+
+int Item_func_boundary::Transporter::start_ring()
+{
+ n_points= 0;
+ return m_receiver->start_shape(Gcalc_function::shape_line);
+}
+
+
+int Item_func_boundary::Transporter::complete_ring()
+{
+ if (n_points > 1)
+ {
+ m_receiver->add_point(last_x, last_y);
+ }
+ m_receiver->complete_shape();
+ return 0;
+}
+
+
+int Item_func_boundary::Transporter::add_point(double x, double y)
+{
+ ++n_points;
+ if (current_type== Gcalc_function::shape_polygon)
+ {
+ /* Polygon's ring case */
+ if (n_points == 1)
+ {
+ last_x= x;
+ last_y= y;
+ }
+ return m_receiver->add_point(x, y);
+ }
+
+ if (current_type== Gcalc_function::shape_line)
+ {
+ /* Line's case */
+ last_x= x;
+ last_y= y;
+ if (n_points == 1)
+ return m_receiver->single_point(x, y);
+ }
+ return 0;
+}
+
+
+int Item_func_boundary::Transporter::start_collection(int n_objects)
+{
+ return 0;
+}
+
+
+String *Item_func_boundary::val_str(String *str_value)
+{
+ DBUG_ENTER("Item_func_boundary::val_str");
+ DBUG_ASSERT(fixed == 1);
+ String arg_val;
+ String *swkb= args[0]->val_str(&arg_val);
+ Geometry_buffer buffer;
+ Geometry *g;
+ uint32 srid= 0;
+ Transporter trn(&res_receiver);
+
+ if ((null_value=
+ args[0]->null_value ||
+ !(g= Geometry::construct(&buffer, swkb->ptr(), swkb->length()))))
+ DBUG_RETURN(0);
+
+ if (g->store_shapes(&trn))
+ goto mem_error;
+
+ str_value->set_charset(&my_charset_bin);
+ if (str_value->reserve(SRID_SIZE, 512))
+ goto mem_error;
+ str_value->length(0);
+ str_value->q_append(srid);
+
+ if (!Geometry::create_from_opresult(&buffer, str_value, res_receiver))
+ goto mem_error;
+
+ res_receiver.reset();
+ DBUG_RETURN(str_value);
+
+mem_error:
+ null_value= 1;
+ DBUG_RETURN(0);
+}
+
+
Field::geometry_type Item_func_centroid::get_geometry_type() const
{
return Field::GEOM_POINT;
@@ -248,6 +372,289 @@ String *Item_func_centroid::val_str(String *str)
}
+int Item_func_convexhull::add_node_to_line(ch_node **p_cur, int dir,
+ const Gcalc_heap::Info *pi)
+{
+ ch_node *new_node;
+ ch_node *cur= *p_cur;
+
+ while (cur->prev)
+ {
+ int v_sign= Gcalc_scan_iterator::point::cmp_dx_dy(
+ cur->prev->pi, cur->pi, cur->pi, pi);
+ if (v_sign*dir <0)
+ break;
+ new_node= cur;
+ cur= cur->prev;
+ res_heap.free_item(new_node);
+ }
+ if (!(new_node= new_ch_node()))
+ return 1;
+ cur->next= new_node;
+ new_node->prev= cur;
+ new_node->pi= pi;
+ *p_cur= new_node;
+ return 0;
+}
+
+
+#ifndef HEAVY_CONVEX_HULL
+String *Item_func_convexhull::val_str(String *str_value)
+{
+ Geometry_buffer buffer;
+ Geometry *geom= NULL;
+ MBR mbr;
+ const char *c_end;
+ Gcalc_operation_transporter trn(&func, &collector);
+ uint32 srid= 0;
+ ch_node *left_first, *left_cur, *right_first, *right_cur;
+ Gcalc_heap::Info *cur_pi;
+
+ DBUG_ENTER("Item_func_convexhull::val_str");
+ DBUG_ASSERT(fixed == 1);
+ String *swkb= args[0]->val_str(&tmp_value);
+
+ if ((null_value=
+ args[0]->null_value ||
+ !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length()))))
+ DBUG_RETURN(0);
+
+ geom->get_mbr(&mbr, &c_end);
+ collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
+ if ((null_value= geom->store_shapes(&trn)))
+ {
+ str_value= 0;
+ goto mem_error;
+ }
+
+ collector.prepare_operation();
+ if (!(cur_pi= collector.get_first()))
+ goto build_result; /* An EMPTY GEOMETRY */
+
+ if (!cur_pi->get_next())
+ {
+ /* Single point. */
+ if (res_receiver.single_point(cur_pi->x, cur_pi->y))
+ goto mem_error;
+ goto build_result;
+ }
+
+ left_cur= left_first= new_ch_node();
+ right_cur= right_first= new_ch_node();
+ right_first->prev= left_first->prev= 0;
+ right_first->pi= left_first->pi= cur_pi;
+
+ while ((cur_pi= cur_pi->get_next()))
+ {
+ /* Handle left part of the hull, then the right part. */
+ if (add_node_to_line(&left_cur, 1, cur_pi))
+ goto mem_error;
+ if (add_node_to_line(&right_cur, -1, cur_pi))
+ goto mem_error;
+ }
+
+ left_cur->next= 0;
+ if (left_first->get_next()->get_next() == NULL &&
+ right_cur->prev->prev == NULL)
+ {
+ /* We only have 2 nodes in the result, so we create a polyline. */
+ if (res_receiver.start_shape(Gcalc_function::shape_line) ||
+ res_receiver.add_point(left_first->pi->x, left_first->pi->y) ||
+ res_receiver.add_point(left_cur->pi->x, left_cur->pi->y) ||
+ res_receiver.complete_shape())
+
+ goto mem_error;
+
+ goto build_result;
+ }
+
+ if (res_receiver.start_shape(Gcalc_function::shape_polygon))
+ goto mem_error;
+
+ while (left_first)
+ {
+ if (res_receiver.add_point(left_first->pi->x, left_first->pi->y))
+ goto mem_error;
+ left_first= left_first->get_next();
+ }
+
+ /* Skip last point in the right part as it coincides */
+ /* with the last one in the left. */
+ right_cur= right_cur->prev;
+ while (right_cur->prev)
+ {
+ if (res_receiver.add_point(right_cur->pi->x, right_cur->pi->y))
+ goto mem_error;
+ right_cur= right_cur->prev;
+ }
+ res_receiver.complete_shape();
+
+build_result:
+ str_value->set_charset(&my_charset_bin);
+ if (str_value->reserve(SRID_SIZE, 512))
+ goto mem_error;
+ str_value->length(0);
+ str_value->q_append(srid);
+
+ if (!Geometry::create_from_opresult(&buffer, str_value, res_receiver))
+ goto mem_error;
+
+mem_error:
+ collector.reset();
+ func.reset();
+ res_receiver.reset();
+ res_heap.reset();
+ DBUG_RETURN(str_value);
+}
+
+#else /*HEAVY_CONVEX_HULL*/
+String *Item_func_convexhull::val_str(String *str_value)
+{
+ Geometry_buffer buffer;
+ Geometry *geom= NULL;
+ MBR mbr;
+ const char *c_end;
+ Gcalc_operation_transporter trn(&func, &collector);
+ const Gcalc_scan_iterator::event_point *ev;
+ uint32 srid= 0;
+ ch_node *left_first, *left_cur, *right_first, *right_cur;
+
+ DBUG_ENTER("Item_func_convexhull::val_str");
+ DBUG_ASSERT(fixed == 1);
+ String *swkb= args[0]->val_str(&tmp_value);
+
+ if ((null_value=
+ args[0]->null_value ||
+ !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length()))))
+ DBUG_RETURN(0);
+
+ geom->get_mbr(&mbr, &c_end);
+ collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
+ if ((null_value= geom->store_shapes(&trn)))
+ {
+ str_value= 0;
+ goto mem_error;
+ }
+
+ collector.prepare_operation();
+ scan_it.init(&collector);
+ scan_it.killed= (int *) &(current_thd->killed);
+
+ if (!scan_it.more_points())
+ goto build_result; /* An EMPTY GEOMETRY */
+
+ if (scan_it.step())
+ goto mem_error;
+
+ if (!scan_it.more_points())
+ {
+ /* Single point. */
+ if (res_receiver.single_point(scan_it.get_events()->pi->x,
+ scan_it.get_events()->pi->y))
+ goto mem_error;
+ goto build_result;
+ }
+
+ left_cur= left_first= new_ch_node();
+ right_cur= right_first= new_ch_node();
+ right_first->prev= left_first->prev= 0;
+ right_first->pi= left_first->pi= scan_it.get_events()->pi;
+
+ while (scan_it.more_points())
+ {
+ if (scan_it.step())
+ goto mem_error;
+ ev= scan_it.get_events();
+
+ /* Skip the intersections-only events. */
+ while (ev->event == scev_intersection)
+ {
+ ev= ev->get_next();
+ if (!ev)
+ goto skip_point;
+ }
+
+ {
+ Gcalc_point_iterator pit(&scan_it);
+ if (!pit.point() || scan_it.get_event_position() == pit.point())
+ {
+ /* Handle left part of the hull. */
+ if (add_node_to_line(&left_cur, 1, ev->pi))
+ goto mem_error;
+ }
+ if (pit.point())
+ {
+ /* Check the rightmost point */
+ for(; pit.point()->c_get_next(); ++pit)
+ ;
+ }
+ if (!pit.point() || pit.point()->event ||
+ scan_it.get_event_position() == pit.point()->c_get_next())
+ {
+ /* Handle right part of the hull. */
+ if (add_node_to_line(&right_cur, -1, ev->pi))
+ goto mem_error;
+ }
+ }
+skip_point:;
+ }
+
+ left_cur->next= 0;
+ if (left_first->get_next()->get_next() == NULL &&
+ right_cur->prev->prev == NULL)
+ {
+ /* We only have 2 nodes in the result, so we create a polyline. */
+ if (res_receiver.start_shape(Gcalc_function::shape_line) ||
+ res_receiver.add_point(left_first->pi->x, left_first->pi->y) ||
+ res_receiver.add_point(left_cur->pi->x, left_cur->pi->y) ||
+ res_receiver.complete_shape())
+
+ goto mem_error;
+
+ goto build_result;
+ }
+
+ if (res_receiver.start_shape(Gcalc_function::shape_polygon))
+ goto mem_error;
+
+ while (left_first)
+ {
+ if (res_receiver.add_point(left_first->pi->x, left_first->pi->y))
+ goto mem_error;
+ left_first= left_first->get_next();
+ }
+
+ /* Skip last point in the right part as it coincides */
+ /* with the last one in the left. */
+ right_cur= right_cur->prev;
+ while (right_cur->prev)
+ {
+ if (res_receiver.add_point(right_cur->pi->x, right_cur->pi->y))
+ goto mem_error;
+ right_cur= right_cur->prev;
+ }
+ res_receiver.complete_shape();
+
+build_result:
+ str_value->set_charset(&my_charset_bin);
+ if (str_value->reserve(SRID_SIZE, 512))
+ goto mem_error;
+ str_value->length(0);
+ str_value->q_append(srid);
+
+ if (!Geometry::create_from_opresult(&buffer, str_value, res_receiver))
+ goto mem_error;
+
+mem_error:
+ collector.reset();
+ func.reset();
+ res_receiver.reset();
+ res_heap.reset();
+ DBUG_RETURN(str_value);
+}
+#endif /*HEAVY_CONVEX_HULL*/
+
+
/*
Spatial decomposition functions
*/
@@ -593,12 +1000,17 @@ longlong Item_func_spatial_mbr_rel::val_int()
Item_func_spatial_rel::Item_func_spatial_rel(Item *a,Item *b,
enum Functype sp_rel) :
- Item_bool_func2(a,b), collector()
+ Item_int_func(a,b), collector()
{
spatial_rel = sp_rel;
}
+Item_func_spatial_rel::Item_func_spatial_rel(Item *a,Item *b, Item *mask) :
+ Item_int_func(a,b,mask), spatial_rel(SP_RELATE_FUNC)
+{}
+
+
Item_func_spatial_rel::~Item_func_spatial_rel()
{
}
@@ -623,6 +1035,8 @@ const char *Item_func_spatial_rel::func_name() const
return "st_crosses";
case SP_OVERLAPS_FUNC:
return "st_overlaps";
+ case SP_RELATE_FUNC:
+ return "st_relate";
default:
DBUG_ASSERT(0); // Should never happened
return "sp_unknown";
@@ -661,6 +1075,84 @@ static double distance_points(const Gcalc_heap::Info *a,
}
+static Gcalc_function::op_type op_matrix(int n)
+{
+ switch (n)
+ {
+ case 0:
+ return Gcalc_function::op_border;
+ case 1:
+ return Gcalc_function::op_internals;
+ case 2:
+ return (Gcalc_function::op_type)
+ ((int) Gcalc_function::op_not | (int) Gcalc_function::op_union);
+ };
+ GCALC_DBUG_ASSERT(FALSE);
+ return Gcalc_function::op_any;
+}
+
+
+static int setup_relate_func(Geometry *g1, Geometry *g2,
+ Gcalc_operation_transporter *trn, Gcalc_function *func,
+ const char *mask)
+{
+ int do_store_shapes=1;
+ uint shape_a, shape_b;
+ uint n_operands= 0;
+ int last_shape_pos;
+
+ last_shape_pos= func->get_next_expression_pos();
+ func->add_operation(Gcalc_function::op_intersection, 0);
+ for (int nc=0; nc<9; nc++)
+ {
+ uint cur_op;
+
+ cur_op= Gcalc_function::op_intersection;
+ switch (mask[nc])
+ {
+ case '*':
+ continue;
+ case 'T':
+ case '0':
+ case '1':
+ case '2':
+ cur_op|= Gcalc_function::v_find_t;
+ break;
+ case 'F':
+ cur_op|= Gcalc_function::v_find_f;
+ break;
+ };
+ ++n_operands;
+ if (func->reserve_op_buffer(1))
+ return 1;
+ func->add_operation(cur_op, 2);
+
+ func->add_operation(op_matrix(nc/3), 1);
+ if (do_store_shapes)
+ {
+ shape_a= func->get_next_expression_pos();
+ if (g1->store_shapes(trn))
+ return 1;
+ }
+ else
+ func->repeat_expression(shape_a);
+ func->add_operation(op_matrix(nc%3), 1);
+ if (do_store_shapes)
+ {
+ shape_b= func->get_next_expression_pos();
+ if (g2->store_shapes(trn))
+ return 1;
+ do_store_shapes= 0;
+ }
+ else
+ func->repeat_expression(shape_b);
+ }
+
+ func->add_operands_to_op(last_shape_pos, n_operands);
+ return 0;
+}
+
+
#define GIS_ZERO 0.00000000001
longlong Item_func_spatial_rel::val_int()
@@ -669,6 +1161,7 @@ longlong Item_func_spatial_rel::val_int()
DBUG_ASSERT(fixed == 1);
String *res1;
String *res2;
+ String *res3;
Geometry_buffer buffer1, buffer2;
Geometry *g1, *g2;
int result= 0;
@@ -736,6 +1229,8 @@ longlong Item_func_spatial_rel::val_int()
case SP_OVERLAPS_FUNC:
case SP_CROSSES_FUNC:
func.add_operation(Gcalc_function::op_intersection, 2);
+ if (func.reserve_op_buffer(1))
+ break;
func.add_operation(Gcalc_function::v_find_t |
Gcalc_function::op_intersection, 2);
shape_a= func.get_next_expression_pos();
@@ -744,6 +1239,8 @@ longlong Item_func_spatial_rel::val_int()
shape_b= func.get_next_expression_pos();
if ((null_value= g2->store_shapes(&trn)))
break;
+ if (func.reserve_op_buffer(7))
+ break;
func.add_operation(Gcalc_function::v_find_t |
Gcalc_function::op_intersection, 2);
func.add_operation(Gcalc_function::v_find_t |
@@ -756,6 +1253,8 @@ longlong Item_func_spatial_rel::val_int()
func.repeat_expression(shape_a);
break;
case SP_TOUCHES_FUNC:
+ if (func.reserve_op_buffer(2))
+ break;
func.add_operation(Gcalc_function::op_intersection, 2);
func.add_operation(Gcalc_function::v_find_f |
Gcalc_function::op_not |
@@ -775,6 +1274,13 @@ longlong Item_func_spatial_rel::val_int()
func.add_operation(Gcalc_function::op_border, 1);
func.repeat_expression(shape_b);
break;
+ case SP_RELATE_FUNC:
+ res3= args[2]->val_str(&tmp_matrix);
+ if ((null_value= args[2]->null_value))
+ break;
+ null_value= (res3->length() != 9) ||
+ setup_relate_func(g1, g2, &trn, &func, res3->ptr());
+ break;
default:
DBUG_ASSERT(FALSE);
break;
@@ -859,7 +1365,7 @@ String *Item_func_spatial_operation::val_str(String *str_value)
str_value->length(0);
str_value->q_append(srid);
- if (Geometry::create_from_opresult(&buffer1, str_value, res_receiver))
+ if (!Geometry::create_from_opresult(&buffer1, str_value, res_receiver))
goto exit;
exit:
@@ -1319,7 +1825,7 @@ String *Item_func_buffer::val_str(String *str_value)
str_value->length(0);
str_value->q_append(srid);
- if (Geometry::create_from_opresult(&buffer, str_value, res_receiver))
+ if (!Geometry::create_from_opresult(&buffer, str_value, res_receiver))
goto mem_error;
null_value= 0;
@@ -1423,6 +1929,30 @@ longlong Item_func_isclosed::val_int()
return (longlong) isclosed;
}
+
+longlong Item_func_isring::val_int()
+{
+ /* It's actually a combination of two functions - IsClosed and IsSimple */
+ DBUG_ASSERT(fixed == 1);
+ String tmp;
+ String *swkb= args[0]->val_str(&tmp);
+ Geometry_buffer buffer;
+ Geometry *geom;
+ int isclosed= 0; // In case of error
+
+ null_value= (!swkb ||
+ args[0]->null_value ||
+ !(geom=
+ Geometry::construct(&buffer, swkb->ptr(), swkb->length())) ||
+ geom->is_closed(&isclosed));
+
+ if (!isclosed)
+ return 0;
+
+ return Item_func_issimple::val_int();
+}
+
+
/*
Numerical functions
*/
@@ -1739,6 +2269,121 @@ mem_error:
}
+String *Item_func_pointonsurface::val_str(String *str)
+{
+ Gcalc_operation_transporter trn(&func, &collector);
+
+ DBUG_ENTER("Item_func_pointonsurface::val_real");
+ DBUG_ASSERT(fixed == 1);
+ String *res= args[0]->val_str(&tmp_value);
+ Geometry_buffer buffer;
+ Geometry *g;
+ MBR mbr;
+ const char *c_end;
+ double px, py, x0, y0;
+ String *result= 0;
+ const Gcalc_scan_iterator::point *pprev= NULL;
+ uint32 srid;
+
+
+ null_value= 1;
+ if ((args[0]->null_value ||
+ !(g= Geometry::construct(&buffer, res->ptr(), res->length())) ||
+ g->get_mbr(&mbr, &c_end)))
+ goto mem_error;
+
+ collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
+
+ if (g->store_shapes(&trn))
+ goto mem_error;
+
+ collector.prepare_operation();
+ scan_it.init(&collector);
+
+ while (scan_it.more_points())
+ {
+ if (scan_it.step())
+ goto mem_error;
+
+ if (scan_it.get_h() > GIS_ZERO)
+ {
+ y0= scan_it.get_y();
+ break;
+ }
+ }
+
+ if (!scan_it.more_points())
+ {
+ goto exit;
+ }
+
+ if (scan_it.step())
+ goto mem_error;
+
+ for (Gcalc_point_iterator pit(&scan_it); pit.point(); ++pit)
+ {
+ if (pprev == NULL)
+ {
+ pprev= pit.point();
+ continue;
+ }
+ x0= scan_it.get_sp_x(pprev);
+ px= scan_it.get_sp_x(pit.point());
+ if (px - x0 > GIS_ZERO)
+ {
+ if (scan_it.get_h() > GIS_ZERO)
+ {
+ px= (px + x0) / 2.0;
+ py= scan_it.get_y();
+ }
+ else
+ {
+ px= (px + x0) / 2.0;
+ py= (y0 + scan_it.get_y()) / 2.0;
+ }
+ null_value= 0;
+ break;
+ }
+ pprev= NULL;
+ }
+
+ if (null_value)
+ goto exit;
+
+ str->set_charset(&my_charset_bin);
+ if (str->reserve(SRID_SIZE, 512))
+ goto mem_error;
+
+ str->length(0);
+ srid= uint4korr(res->ptr());
+ str->q_append(srid);
+
+ if (Geometry::create_point(str, px, py))
+ goto mem_error;
+
+ result= str;
+
+exit:
+ collector.reset();
+ func.reset();
+ scan_it.reset();
+ DBUG_RETURN(result);
+
+mem_error:
+ collector.reset();
+ func.reset();
+ scan_it.reset();
+ null_value= 1;
+ DBUG_RETURN(0);
+}
+
+
+Field::geometry_type Item_func_pointonsurface::get_geometry_type() const
+{
+ return Field::GEOM_POINT;
+}
+
+
#ifndef DBUG_OFF
longlong Item_func_gis_debug::val_int()
{