diff options
Diffstat (limited to 'sql/item_geofunc.cc')
-rw-r--r-- | sql/item_geofunc.cc | 651 |
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() { |