diff options
author | Sergei Golubchik <sergii@pisem.net> | 2011-11-27 17:46:20 +0100 |
---|---|---|
committer | Sergei Golubchik <sergii@pisem.net> | 2011-11-27 17:46:20 +0100 |
commit | effed09bd7d8081704eaa017060da84c32e3bf29 (patch) | |
tree | 9dd712312526cdbac1ab622efcdfc28e3fce965a /sql/item_geofunc.cc | |
parent | 7189f09aa6d434fc889cb6d819e97c09f8cc0bcf (diff) | |
parent | 12e60c4989ce0214da88faad7c08d2f046885327 (diff) | |
download | mariadb-git-effed09bd7d8081704eaa017060da84c32e3bf29.tar.gz |
5.3->5.5 merge
Diffstat (limited to 'sql/item_geofunc.cc')
-rw-r--r-- | sql/item_geofunc.cc | 1092 |
1 files changed, 1077 insertions, 15 deletions
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 621ddcb1a30..5ccdb198ecb 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. + Copyright (C) 2011 Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,6 +37,7 @@ #ifdef HAVE_SPATIAL #include <m_ctype.h> + Field *Item_geometry_func::tmp_table_field(TABLE *t_arg) { Field *result; @@ -368,8 +370,8 @@ String *Item_func_point::val_str(String *str) uint32 srid= 0; if ((null_value= (args[0]->null_value || - args[1]->null_value || - str->realloc(4/*SRID*/ + 1 + 4 + SIZEOF_STORED_DOUBLE*2)))) + args[1]->null_value || + str->realloc(4/*SRID*/ + 1 + 4 + SIZEOF_STORED_DOUBLE * 2)))) return 0; str->set_charset(&my_charset_bin); @@ -516,7 +518,33 @@ err: Functions for spatial relations */ -longlong Item_func_spatial_rel::val_int() +const char *Item_func_spatial_mbr_rel::func_name() const +{ + switch (spatial_rel) { + case SP_CONTAINS_FUNC: + return "mbrcontains"; + case SP_WITHIN_FUNC: + return "mbrwithin"; + case SP_EQUALS_FUNC: + return "mbrequals"; + case SP_DISJOINT_FUNC: + return "mbrdisjoint"; + case SP_INTERSECTS_FUNC: + return "mbrintersects"; + case SP_TOUCHES_FUNC: + return "mbrtouches"; + case SP_CROSSES_FUNC: + return "mbrcrosses"; + case SP_OVERLAPS_FUNC: + return "mbroverlaps"; + default: + DBUG_ASSERT(0); // Should never happened + return "mbrsp_unknown"; + } +} + + +longlong Item_func_spatial_mbr_rel::val_int() { DBUG_ASSERT(fixed == 1); String *res1= args[0]->val_str(&cmp.value1); @@ -561,6 +589,844 @@ longlong Item_func_spatial_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() +{ + spatial_rel = sp_rel; +} + + +Item_func_spatial_rel::~Item_func_spatial_rel() +{ +} + + +const char *Item_func_spatial_rel::func_name() const +{ + switch (spatial_rel) { + case SP_CONTAINS_FUNC: + return "st_contains"; + case SP_WITHIN_FUNC: + return "st_within"; + case SP_EQUALS_FUNC: + return "st_equals"; + case SP_DISJOINT_FUNC: + return "st_disjoint"; + case SP_INTERSECTS_FUNC: + return "st_intersects"; + case SP_TOUCHES_FUNC: + return "st_touches"; + case SP_CROSSES_FUNC: + return "st_crosses"; + case SP_OVERLAPS_FUNC: + return "st_overlaps"; + default: + DBUG_ASSERT(0); // Should never happened + return "sp_unknown"; + } +} + + +static double count_edge_t(const Gcalc_heap::Info *ea, + const Gcalc_heap::Info *eb, + const Gcalc_heap::Info *v, + double &ex, double &ey, double &vx, double &vy, + double &e_sqrlen) +{ + ex= eb->x - ea->x; + ey= eb->y - ea->y; + vx= v->x - ea->x; + vy= v->y - ea->y; + e_sqrlen= ex * ex + ey * ey; + return (ex * vx + ey * vy) / e_sqrlen; +} + + +static double distance_to_line(double ex, double ey, double vx, double vy, + double e_sqrlen) +{ + return fabs(vx * ey - vy * ex) / sqrt(e_sqrlen); +} + + +static double distance_points(const Gcalc_heap::Info *a, + const Gcalc_heap::Info *b) +{ + double x= a->x - b->x; + double y= a->y - b->y; + return sqrt(x * x + y * y); +} + + +/* + Calculates the distance between objects. +*/ + +#ifdef TMP_BLOCK +static int calc_distance(double *result, Gcalc_heap *collector, uint obj2_si, + Gcalc_function *func, Gcalc_scan_iterator *scan_it) +{ + bool above_cur_point, cur_point_edge; + const Gcalc_scan_iterator::point *evpos; + const Gcalc_heap::Info *cur_point= NULL; + const Gcalc_heap::Info *dist_point; + const Gcalc_scan_iterator::point *ev; + double t, distance, cur_distance; + double ex, ey, vx, vy, e_sqrlen; + int o1, o2; + + DBUG_ENTER("calc_distance"); + + above_cur_point= false; + distance= DBL_MAX; + + while (scan_it->more_points()) + { + if (scan_it->step()) + goto mem_error; + evpos= scan_it->get_event_position(); + ev= scan_it->get_events(); + cur_point= NULL; + + if (ev->simple_event()) + { + cur_point= ev->pi; + goto calculate_distance; + } + + /* + handling intersection we only need to check if it's the intersecion + of objects 1 and 2. In this case distance is 0 + */ + o1= 0; + o2= 0; + for (; ev; ev= ev->get_next()) + { + if (ev->event != scev_intersection) + cur_point= ev->pi; + if (ev->pi->shape >= obj2_si) + o2= 1; + else + o1= 1; + if (o1 && o2) + { + distance= 0; + goto exit; + } + } + if (!cur_point) + continue; + +#ifdef TO_REMOVE + goto calculate_distance; + /* + having these events we need to check for possible intersection + of objects + scev_thread | scev_two_threads | scev_single_point + */ + DBUG_ASSERT(ev & (scev_thread | scev_two_threads | scev_single_point)); + + func->clear_state(); + for (Gcalc_point_iterator pit(scan_it); pit.point() != evpos; ++pit) + { + gcalc_shape_info si= pit.point()->get_shape(); + if ((func->get_shape_kind(si) == Gcalc_function::shape_polygon)) + func->invert_state(si); + } + func->invert_state(evpos->get_shape()); + if (func->count()) + { + /* Point of one object is inside the other - intersection found */ + distance= 0; + goto exit; + } +#endif /*TO_REMOVE*/ + +calculate_distance: + if (cur_point->shape >= obj2_si) + continue; + cur_point_edge= !cur_point->is_bottom(); + + for (dist_point= collector->get_first(); dist_point; dist_point= dist_point->get_next()) + { + /* We only check vertices of object 2 */ + if (dist_point->shape < obj2_si) + continue; + + /* if we have an edge to check */ + if (dist_point->left) + { + t= count_edge_t(dist_point, dist_point->left, cur_point, + ex, ey, vx, vy, e_sqrlen); + if ((t > 0.0) && (t < 1.0)) + { + cur_distance= distance_to_line(ex, ey, vx, vy, e_sqrlen); + if (distance > cur_distance) + distance= cur_distance; + } + } + if (cur_point_edge) + { + t= count_edge_t(cur_point, cur_point->left, dist_point, + ex, ey, vx, vy, e_sqrlen); + if ((t > 0.0) && (t < 1.0)) + { + cur_distance= distance_to_line(ex, ey, vx, vy, e_sqrlen); + if (distance > cur_distance) + distance= cur_distance; + } + } + cur_distance= distance_points(cur_point, dist_point); + if (distance > cur_distance) + distance= cur_distance; + } + } + +exit: + *result= distance; + DBUG_RETURN(0); + +mem_error: + DBUG_RETURN(1); +} +#endif /*TMP_BLOCK*/ + + +#define GIS_ZERO 0.00000000001 + +longlong Item_func_spatial_rel::val_int() +{ + DBUG_ENTER("Item_func_spatial_rel::val_int"); + DBUG_ASSERT(fixed == 1); + String *res1; + String *res2; + Geometry_buffer buffer1, buffer2; + Geometry *g1, *g2; + int result= 0; + int mask= 0; + uint shape_a, shape_b; + + res1= args[0]->val_str(&tmp_value1); + res2= args[1]->val_str(&tmp_value2); + Gcalc_operation_transporter trn(&func, &collector); + + if (func.reserve_op_buffer(1)) + DBUG_RETURN(0); + + if ((null_value= + (args[0]->null_value || args[1]->null_value || + !(g1= Geometry::construct(&buffer1, res1->ptr(), res1->length())) || + !(g2= Geometry::construct(&buffer2, res2->ptr(), res2->length()))))) + goto exit; + + switch (spatial_rel) { + case SP_CONTAINS_FUNC: + mask= 1; + func.add_operation(Gcalc_function::op_difference, 2); + /* Mind the g2 goes first. */ + null_value= g2->store_shapes(&trn) || g1->store_shapes(&trn); + break; + case SP_WITHIN_FUNC: + mask= 1; + func.add_operation(Gcalc_function::op_difference, 2); + null_value= g1->store_shapes(&trn) || g2->store_shapes(&trn); + break; + case SP_EQUALS_FUNC: + mask= 1; + func.add_operation(Gcalc_function::op_symdifference, 2); + null_value= g1->store_shapes(&trn) || g2->store_shapes(&trn); + break; + case SP_DISJOINT_FUNC: + mask= 1; + func.add_operation(Gcalc_function::op_intersection, 2); + null_value= g1->store_shapes(&trn) || g2->store_shapes(&trn); + break; + case SP_INTERSECTS_FUNC: + func.add_operation(Gcalc_function::op_intersection, 2); + null_value= g1->store_shapes(&trn) || g2->store_shapes(&trn); + break; + case SP_OVERLAPS_FUNC: + case SP_CROSSES_FUNC: + func.add_operation(Gcalc_function::op_intersection, 2); + func.add_operation(Gcalc_function::v_find_t | + Gcalc_function::op_intersection, 2); + shape_a= func.get_next_expression_pos(); + if ((null_value= g1->store_shapes(&trn))) + break; + shape_b= func.get_next_expression_pos(); + if ((null_value= g2->store_shapes(&trn))) + break; + func.add_operation(Gcalc_function::v_find_t | + Gcalc_function::op_intersection, 2); + func.add_operation(Gcalc_function::v_find_t | + Gcalc_function::op_difference, 2); + func.repeat_expression(shape_a); + func.repeat_expression(shape_b); + func.add_operation(Gcalc_function::v_find_t | + Gcalc_function::op_difference, 2); + func.repeat_expression(shape_b); + func.repeat_expression(shape_a); + break; + case SP_TOUCHES_FUNC: + func.add_operation(Gcalc_function::op_intersection, 2); + func.add_operation(Gcalc_function::v_find_f | + Gcalc_function::op_not | + Gcalc_function::op_intersection, 2); + func.add_operation(Gcalc_function::op_internals, 1); + shape_a= func.get_next_expression_pos(); + if ((null_value= g1->store_shapes(&trn))) + break; + func.add_operation(Gcalc_function::op_internals, 1); + shape_b= func.get_next_expression_pos(); + if ((null_value= g2->store_shapes(&trn))) + break; + func.add_operation(Gcalc_function::v_find_t | + Gcalc_function::op_intersection, 2); + func.add_operation(Gcalc_function::op_border, 1); + func.repeat_expression(shape_a); + func.add_operation(Gcalc_function::op_border, 1); + func.repeat_expression(shape_b); + break; + default: + DBUG_ASSERT(FALSE); + break; + } + + if (null_value) + goto exit; + + collector.prepare_operation(); + scan_it.init(&collector); + scan_it.killed= (int *) &(current_thd->killed); + +#ifdef TMP_BLOCK + if (spatial_rel == SP_EQUALS_FUNC) + { + result= (g1->get_class_info()->m_type_id == g1->get_class_info()->m_type_id) && + func_equals(); + goto exit; + } +#endif /*TMP_BLOCK*/ + + if (func.alloc_states()) + goto exit; + + result= func.check_function(scan_it) ^ mask; + +exit: + collector.reset(); + func.reset(); + scan_it.reset(); + DBUG_RETURN(result); +} + + +Item_func_spatial_operation::~Item_func_spatial_operation() +{ +} + + +String *Item_func_spatial_operation::val_str(String *str_value) +{ + DBUG_ENTER("Item_func_spatial_operation::val_str"); + DBUG_ASSERT(fixed == 1); + String *res1= args[0]->val_str(&tmp_value1); + String *res2= args[1]->val_str(&tmp_value2); + Geometry_buffer buffer1, buffer2; + Geometry *g1, *g2; + uint32 srid= 0; + Gcalc_operation_transporter trn(&func, &collector); + + if (func.reserve_op_buffer(1)) + DBUG_RETURN(0); + func.add_operation(spatial_op, 2); + + if ((null_value= + (args[0]->null_value || args[1]->null_value || + !(g1= Geometry::construct(&buffer1, res1->ptr(), res1->length())) || + !(g2= Geometry::construct(&buffer2, res2->ptr(), res2->length())) || + g1->store_shapes(&trn) || g2->store_shapes(&trn)))) + { + str_value= 0; + goto exit; + } + + + collector.prepare_operation(); + if (func.alloc_states()) + goto exit; + + operation.init(&func); + + if (operation.count_all(&collector) || + operation.get_result(&res_receiver)) + goto exit; + + + str_value->set_charset(&my_charset_bin); + if (str_value->reserve(SRID_SIZE, 512)) + goto exit; + str_value->length(0); + str_value->q_append(srid); + + if (!Geometry::create_from_opresult(&buffer1, str_value, res_receiver)) + goto exit; + +exit: + collector.reset(); + func.reset(); + res_receiver.reset(); + DBUG_RETURN(str_value); +} + + +const char *Item_func_spatial_operation::func_name() const +{ + switch (spatial_op) { + case Gcalc_function::op_intersection: + return "st_intersection"; + case Gcalc_function::op_difference: + return "st_difference"; + case Gcalc_function::op_union: + return "st_union"; + case Gcalc_function::op_symdifference: + return "st_symdifference"; + default: + DBUG_ASSERT(0); // Should never happen + return "sp_unknown"; + } +} + + +static const int SINUSES_CALCULATED= 32; +static double n_sinus[SINUSES_CALCULATED+1]= +{ + 0, + 0.04906767432741802, + 0.0980171403295606, + 0.1467304744553618, + 0.1950903220161283, + 0.2429801799032639, + 0.2902846772544623, + 0.3368898533922201, + 0.3826834323650898, + 0.4275550934302821, + 0.4713967368259976, + 0.5141027441932217, + 0.5555702330196022, + 0.5956993044924334, + 0.6343932841636455, + 0.6715589548470183, + 0.7071067811865475, + 0.7409511253549591, + 0.773010453362737, + 0.8032075314806448, + 0.8314696123025452, + 0.8577286100002721, + 0.8819212643483549, + 0.9039892931234433, + 0.9238795325112867, + 0.9415440651830208, + 0.9569403357322089, + 0.970031253194544, + 0.9807852804032304, + 0.989176509964781, + 0.9951847266721968, + 0.9987954562051724, + 1 +}; + + +static void get_n_sincos(int n, double *sinus, double *cosinus) +{ + DBUG_ASSERT(n > 0 && n < SINUSES_CALCULATED*2+1); + if (n < (SINUSES_CALCULATED + 1)) + { + *sinus= n_sinus[n]; + *cosinus= n_sinus[SINUSES_CALCULATED - n]; + } + else + { + n-= SINUSES_CALCULATED; + *sinus= n_sinus[SINUSES_CALCULATED - n]; + *cosinus= -n_sinus[n]; + } +} + + +static int fill_half_circle(Gcalc_shape_transporter *trn, double x, double y, + double ax, double ay) +{ + double n_sin, n_cos; + double x_n, y_n; + for (int n = 1; n < (SINUSES_CALCULATED * 2 - 1); n++) + { + get_n_sincos(n, &n_sin, &n_cos); + x_n= ax * n_cos - ay * n_sin; + y_n= ax * n_sin + ay * n_cos; + if (trn->add_point(x_n + x, y_n + y)) + return 1; + } + return 0; +} + + +static int fill_gap(Gcalc_shape_transporter *trn, + double x, double y, + double ax, double ay, double bx, double by, double d, + bool *empty_gap) +{ + double ab= ax * bx + ay * by; + double cosab= ab / (d * d) + GIS_ZERO; + double n_sin, n_cos; + double x_n, y_n; + int n=1; + + *empty_gap= true; + for (;;) + { + get_n_sincos(n++, &n_sin, &n_cos); + if (n_cos <= cosab) + break; + *empty_gap= false; + x_n= ax * n_cos - ay * n_sin; + y_n= ax * n_sin + ay * n_cos; + if (trn->add_point(x_n + x, y_n + y)) + return 1; + } + return 0; +} + + +/* + Calculates the vector (p2,p1) and + negatively orthogonal to it with the length of d. + The result is (ex,ey) - the vector, (px,py) - the orthogonal. +*/ + +static void calculate_perpendicular( + double x1, double y1, double x2, double y2, double d, + double *ex, double *ey, + double *px, double *py) +{ + double q; + *ex= x1 - x2; + *ey= y1 - y2; + q= d / sqrt((*ex) * (*ex) + (*ey) * (*ey)); + *px= (*ey) * q; + *py= -(*ex) * q; +} + + +int Item_func_buffer::Transporter::single_point(double x, double y) +{ + return add_point_buffer(x, y); +} + + +int Item_func_buffer::Transporter::add_edge_buffer( + double x3, double y3, bool round_p1, bool round_p2) +{ + Gcalc_operation_transporter trn(m_fn, m_heap); + double e1_x, e1_y, e2_x, e2_y, p1_x, p1_y, p2_x, p2_y; + double e1e2; + double sin1, cos1; + double x_n, y_n; + bool empty_gap1, empty_gap2; + + ++m_nshapes; + if (trn.start_simple_poly()) + return 1; + + calculate_perpendicular(x1, y1, x2, y2, m_d, &e1_x, &e1_y, &p1_x, &p1_y); + calculate_perpendicular(x3, y3, x2, y2, m_d, &e2_x, &e2_y, &p2_x, &p2_y); + + e1e2= e1_x * e2_y - e2_x * e1_y; + sin1= n_sinus[1]; + cos1= n_sinus[31]; + if (e1e2 < 0) + { + empty_gap2= false; + x_n= x2 + p2_x * cos1 - p2_y * sin1; + y_n= y2 + p2_y * cos1 + p2_x * sin1; + if (fill_gap(&trn, x2, y2, -p1_x,-p1_y, p2_x,p2_y, m_d, &empty_gap1) || + trn.add_point(x2 + p2_x, y2 + p2_y) || + trn.add_point(x_n, y_n)) + return 1; + } + else + { + x_n= x2 - p2_x * cos1 - p2_y * sin1; + y_n= y2 - p2_y * cos1 + p2_x * sin1; + if (trn.add_point(x_n, y_n) || + trn.add_point(x2 - p2_x, y2 - p2_y) || + fill_gap(&trn, x2, y2, -p2_x, -p2_y, p1_x, p1_y, m_d, &empty_gap2)) + return 1; + empty_gap1= false; + } + if ((!empty_gap2 && trn.add_point(x2 + p1_x, y2 + p1_y)) || + trn.add_point(x1 + p1_x, y1 + p1_y)) + return 1; + + if (round_p1 && fill_half_circle(&trn, x1, y1, p1_x, p1_y)) + return 1; + + if (trn.add_point(x1 - p1_x, y1 - p1_y) || + (!empty_gap1 && trn.add_point(x2 - p1_x, y2 - p1_y))) + return 1; + return trn.complete_simple_poly(); +} + + +int Item_func_buffer::Transporter::add_last_edge_buffer() +{ + Gcalc_operation_transporter trn(m_fn, m_heap); + double e1_x, e1_y, p1_x, p1_y; + + ++m_nshapes; + if (trn.start_simple_poly()) + return 1; + + calculate_perpendicular(x1, y1, x2, y2, m_d, &e1_x, &e1_y, &p1_x, &p1_y); + + if (trn.add_point(x1 + p1_x, y1 + p1_y) || + trn.add_point(x1 - p1_x, y1 - p1_y) || + trn.add_point(x2 - p1_x, y2 - p1_y) || + fill_half_circle(&trn, x2, y2, -p1_x, -p1_y) || + trn.add_point(x2 + p1_x, y2 + p1_y)) + return 1; + return trn.complete_simple_poly(); +} + + +int Item_func_buffer::Transporter::add_point_buffer(double x, double y) +{ + Gcalc_operation_transporter trn(m_fn, m_heap); + + m_nshapes++; + if (trn.start_simple_poly()) + return 1; + if (trn.add_point(x - m_d, y) || + fill_half_circle(&trn, x, y, -m_d, 0.0) || + trn.add_point(x + m_d, y) || + fill_half_circle(&trn, x, y, m_d, 0.0)) + return 1; + return trn.complete_simple_poly(); +} + + +int Item_func_buffer::Transporter::start_line() +{ + if (buffer_op == Gcalc_function::op_difference) + { + skip_line= TRUE; + return 0; + } + + m_nshapes= 0; + + if (m_fn->reserve_op_buffer(2)) + return 1; + last_shape_pos= m_fn->get_next_expression_pos(); + m_fn->add_operation(buffer_op, 0); + m_npoints= 0; + int_start_line(); + return 0; +} + + +int Item_func_buffer::Transporter::start_poly() +{ + m_nshapes= 1; + + if (m_fn->reserve_op_buffer(2)) + return 1; + last_shape_pos= m_fn->get_next_expression_pos(); + m_fn->add_operation(buffer_op, 0); + return Gcalc_operation_transporter::start_poly(); +} + + +int Item_func_buffer::Transporter::complete_poly() +{ + if (Gcalc_operation_transporter::complete_poly()) + return 1; + m_fn->add_operands_to_op(last_shape_pos, m_nshapes); + return 0; +} + + +int Item_func_buffer::Transporter::start_ring() +{ + m_npoints= 0; + return Gcalc_operation_transporter::start_ring(); +} + + +int Item_func_buffer::Transporter::start_collection(int n_objects) +{ + if (m_fn->reserve_op_buffer(1)) + return 1; + m_fn->add_operation(Gcalc_function::op_union, n_objects); + return 0; +} + + +int Item_func_buffer::Transporter::add_point(double x, double y) +{ + if (skip_line) + return 0; + + if (m_npoints && x == x2 && y == y2) + return 0; + + ++m_npoints; + + if (m_npoints == 1) + { + x00= x; + y00= y; + } + else if (m_npoints == 2) + { + x01= x; + y01= y; + } + else if (add_edge_buffer(x, y, (m_npoints == 3) && line_started(), false)) + return 1; + + x1= x2; + y1= y2; + x2= x; + y2= y; + + return line_started() ? 0 : Gcalc_operation_transporter::add_point(x, y); +} + + +int Item_func_buffer::Transporter::complete() +{ + if (m_npoints) + { + if (m_npoints == 1) + { + if (add_point_buffer(x2, y2)) + return 1; + } + else if (m_npoints == 2) + { + if (add_edge_buffer(x1, y1, true, true)) + return 1; + } + else if (line_started()) + { + if (add_last_edge_buffer()) + return 1; + } + else + { + if (x2 != x00 || y2 != y00) + { + if (add_edge_buffer(x00, y00, false, false)) + return 1; + x1= x2; + y1= y2; + x2= x00; + y2= y00; + } + if (add_edge_buffer(x01, y01, false, false)) + return 1; + } + } + + return 0; +} + + +int Item_func_buffer::Transporter::complete_line() +{ + if (!skip_line) + { + if (complete()) + return 1; + int_complete_line(); + m_fn->add_operands_to_op(last_shape_pos, m_nshapes); + } + skip_line= FALSE; + return 0; +} + + +int Item_func_buffer::Transporter::complete_ring() +{ + return complete() || + Gcalc_operation_transporter::complete_ring(); +} + + +String *Item_func_buffer::val_str(String *str_value) +{ + DBUG_ENTER("Item_func_buffer::val_str"); + DBUG_ASSERT(fixed == 1); + String *obj= args[0]->val_str(&tmp_value); + double dist= args[1]->val_real(); + Geometry_buffer buffer; + Geometry *g; + uint32 srid= 0; + String *str_result= NULL; + Transporter trn(&func, &collector, dist); + + null_value= 1; + if (args[0]->null_value || args[1]->null_value || + !(g= Geometry::construct(&buffer, obj->ptr(), obj->length()))) + goto mem_error; + + /* + If the distance given is 0, the Buffer function is in fact NOOP, + so it's natural just to return the argument1. + Besides, internal calculations here can't handle zero distance anyway. + */ + if (fabs(dist) < GIS_ZERO) + { + null_value= 0; + str_result= obj; + goto mem_error; + } + + if (g->store_shapes(&trn)) + goto mem_error; + + collector.prepare_operation(); + if (func.alloc_states()) + goto mem_error; + operation.init(&func); + operation.killed= (int *) &(current_thd->killed); + + if (operation.count_all(&collector) || + operation.get_result(&res_receiver)) + 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; + + null_value= 0; + str_result= str_value; +mem_error: + collector.reset(); + func.reset(); + res_receiver.reset(); + DBUG_RETURN(str_result); +} + + longlong Item_func_isempty::val_int() { DBUG_ASSERT(fixed == 1); @@ -574,21 +1440,59 @@ longlong Item_func_isempty::val_int() } -/** - @todo - Ramil or Holyfoot, add real IsSimple calculation -*/ longlong Item_func_issimple::val_int() { - DBUG_ASSERT(fixed == 1); - String tmp; String *swkb= args[0]->val_str(&tmp); Geometry_buffer buffer; + Gcalc_operation_transporter trn(&func, &collector); + Geometry *g; + int result= 1; + const Gcalc_scan_iterator::event_point *ev; + + DBUG_ENTER("Item_func_issimple::val_int"); + DBUG_ASSERT(fixed == 1); - null_value= args[0]->null_value || - !(Geometry::construct(&buffer, swkb->ptr(), swkb->length())); - /* TODO: Ramil or Holyfoot, add real IsSimple calculation */ - return 0; + if ((null_value= args[0]->null_value) || + !(g= Geometry::construct(&buffer, swkb->ptr(), swkb->length()))) + DBUG_RETURN(0); + + + if (g->get_class_info()->m_type_id == Geometry::wkb_point) + DBUG_RETURN(1); + + 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; + + ev= scan_it.get_events(); + if (ev->simple_event()) + continue; + + if ((ev->event == scev_thread || ev->event == scev_single_point) && + !ev->get_next()) + continue; + + if (ev->event == scev_two_threads && !ev->get_next()->get_next()) + continue; + + result= 0; + break; + } + + collector.reset(); + func.reset(); + scan_it.reset(); + DBUG_RETURN(result); +mem_error: + null_value= 1; + DBUG_RETURN(0); } @@ -736,12 +1640,13 @@ double Item_func_glength::val_real() String *swkb= args[0]->val_str(&value); Geometry_buffer buffer; Geometry *geom; + const char *end; null_value= (!swkb || !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length())) || - geom->geom_length(&res)); + geom->geom_length(&res, &end)); return res; } @@ -760,4 +1665,161 @@ longlong Item_func_srid::val_int() return (longlong) (uint4korr(swkb->ptr())); } + +double Item_func_distance::val_real() +{ + bool cur_point_edge; + const Gcalc_scan_iterator::point *evpos; + const Gcalc_heap::Info *cur_point, *dist_point; + const Gcalc_scan_iterator::event_point *ev; + double t, distance, cur_distance; + double x1, x2, y1, y2; + double ex, ey, vx, vy, e_sqrlen; + uint obj2_si; + Gcalc_operation_transporter trn(&func, &collector); + + DBUG_ENTER("Item_func_distance::val_real"); + DBUG_ASSERT(fixed == 1); + String *res1= args[0]->val_str(&tmp_value1); + String *res2= args[1]->val_str(&tmp_value2); + Geometry_buffer buffer1, buffer2; + Geometry *g1, *g2; + + + if ((null_value= (args[0]->null_value || args[1]->null_value || + !(g1= Geometry::construct(&buffer1, res1->ptr(), res1->length())) || + !(g2= Geometry::construct(&buffer2, res2->ptr(), res2->length()))))) + goto mem_error; + + if ((g1->get_class_info()->m_type_id == Geometry::wkb_point) && + (g2->get_class_info()->m_type_id == Geometry::wkb_point)) + { + if (((Gis_point *) g1)->get_xy(&x1, &y1) || + ((Gis_point *) g2)->get_xy(&x2, &y2)) + goto mem_error; + ex= x2 - x1; + ey= y2 - y1; + DBUG_RETURN(sqrt(ex * ex + ey * ey)); + } + + if (func.reserve_op_buffer(1)) + goto mem_error; + func.add_operation(Gcalc_function::op_intersection, 2); + + if (g1->store_shapes(&trn)) + goto mem_error; + obj2_si= func.get_nshapes(); + if (g2->store_shapes(&trn) || func.alloc_states()) + goto mem_error; + + if (obj2_si == 0 || func.get_nshapes() == obj2_si) + { + distance= 0.0; + null_value= 1; + goto exit; + } + + + collector.prepare_operation(); + scan_it.init(&collector); + + distance= DBL_MAX; + while (scan_it.more_points()) + { + if (scan_it.step()) + goto mem_error; + evpos= scan_it.get_event_position(); + ev= scan_it.get_events(); + + if (ev->simple_event()) + { + cur_point= ev->pi; + goto count_distance; + } + /* + handling intersection we only need to check if it's the intersecion + of objects 1 and 2. In this case distance is 0 + */ + cur_point= NULL; + + /* + having these events we need to check for possible intersection + of objects + scev_thread | scev_two_threads | scev_single_point + */ + func.clear_i_states(); + for (Gcalc_point_iterator pit(&scan_it); pit.point() != evpos; ++pit) + { + gcalc_shape_info si= pit.point()->get_shape(); + if ((func.get_shape_kind(si) == Gcalc_function::shape_polygon)) + func.invert_i_state(si); + } + + func.clear_b_states(); + for (; ev; ev= ev->get_next()) + { + if (ev->event != scev_intersection) + cur_point= ev->pi; + func.set_b_state(ev->get_shape()); + if (func.count()) + { + /* Point of one object is inside the other - intersection found */ + distance= 0; + goto exit; + } + } + + if (!cur_point) + continue; + +count_distance: + if (cur_point->shape >= obj2_si) + continue; + cur_point_edge= !cur_point->is_bottom(); + + for (dist_point= collector.get_first(); dist_point; dist_point= dist_point->get_next()) + { + /* We only check vertices of object 2 */ + if (dist_point->shape < obj2_si) + continue; + + /* if we have an edge to check */ + if (dist_point->left) + { + t= count_edge_t(dist_point, dist_point->left, cur_point, + ex, ey, vx, vy, e_sqrlen); + if ((t>0.0) && (t<1.0)) + { + cur_distance= distance_to_line(ex, ey, vx, vy, e_sqrlen); + if (distance > cur_distance) + distance= cur_distance; + } + } + if (cur_point_edge) + { + t= count_edge_t(cur_point, cur_point->left, dist_point, + ex, ey, vx, vy, e_sqrlen); + if ((t>0.0) && (t<1.0)) + { + cur_distance= distance_to_line(ex, ey, vx, vy, e_sqrlen); + if (distance > cur_distance) + distance= cur_distance; + } + } + cur_distance= distance_points(cur_point, dist_point); + if (distance > cur_distance) + distance= cur_distance; + } + } +exit: + collector.reset(); + func.reset(); + scan_it.reset(); + DBUG_RETURN(distance); +mem_error: + null_value= 1; + DBUG_RETURN(0); +} + + #endif /*HAVE_SPATIAL*/ |