diff options
author | unknown <bar@gw.udmsearch.izhnet.ru> | 2002-02-22 15:24:42 +0400 |
---|---|---|
committer | unknown <bar@gw.udmsearch.izhnet.ru> | 2002-02-22 15:24:42 +0400 |
commit | 32a08516c974045bfc16fb30fcac18bb7ad8fbb8 (patch) | |
tree | a1157e118f45978188d5caf492b320d6302b1f93 /sql/spatial.cc | |
parent | 3d5dc65dfd72083ed159220d03bea942094e8662 (diff) | |
download | mariadb-git-32a08516c974045bfc16fb30fcac18bb7ad8fbb8.tar.gz |
Added GIS extension
BitKeeper/etc/ignore:
Added myisam/rt_test myisam/sp_test to the ignore list
Diffstat (limited to 'sql/spatial.cc')
-rw-r--r-- | sql/spatial.cc | 1435 |
1 files changed, 1435 insertions, 0 deletions
diff --git a/sql/spatial.cc b/sql/spatial.cc new file mode 100644 index 00000000000..32942a70dc2 --- /dev/null +++ b/sql/spatial.cc @@ -0,0 +1,1435 @@ +#include "mysql_priv.h" + + +#define MAX_DIGITS_IN_DOUBLE 16 + +/***************************** GClassInfo *******************************/ + +#define IMPLEMENT_GEOM(class_name, type_id, name) \ +{ \ + (GF_InitFromText) &class_name::init_from_text, \ + (GF_GetDataAsText) &class_name::get_data_as_text, \ + (GF_GetDataSize) &class_name::get_data_size, \ + (GF_GetMBR) &class_name::get_mbr, \ + (GF_GetD) &class_name::get_x, \ + (GF_GetD) &class_name::get_y, \ + (GF_GetD) &class_name::length, \ + (GF_GetD) &class_name::area, \ + (GF_GetI) &class_name::is_closed, \ + (GF_GetUI) &class_name::num_interior_ring, \ + (GF_GetUI) &class_name::num_points, \ + (GF_GetUI) &class_name::num_geometries, \ + (GF_GetUI) &class_name::dimension, \ + (GF_GetWS) &class_name::start_point, \ + (GF_GetWS) &class_name::end_point, \ + (GF_GetWS) &class_name::exterior_ring, \ + (GF_GetWS) &class_name::centroid, \ + (GF_GetUIWS) &class_name::point_n, \ + (GF_GetUIWS) &class_name::interior_ring_n, \ + (GF_GetUIWS) &class_name::geometry_n, \ + class_name::type_id, \ + name, \ + NULL \ +}, + + +static Geometry::GClassInfo ci_collection[] = +{ + IMPLEMENT_GEOM(GPoint, wkbPoint, "POINT") + IMPLEMENT_GEOM(GLineString, wkbLineString, "LINESTRING") + IMPLEMENT_GEOM(GPolygon, wkbPolygon, "POLYGON") + IMPLEMENT_GEOM(GMultiPoint, wkbMultiPoint, "MULTIPOINT") + IMPLEMENT_GEOM(GMultiLineString, wkbMultiLineString, "MULTILINESTRING") + IMPLEMENT_GEOM(GMultiPolygon, wkbMultiPolygon, "MULTIPOLYGON") + IMPLEMENT_GEOM(GGeometryCollection, wkbGeometryCollection, "GEOMETRYCOLLECTION") +}; + +static Geometry::GClassInfo *ci_collection_end = ci_collection + sizeof(ci_collection); + +/***************************** Geometry *******************************/ + +Geometry::GClassInfo *Geometry::find_class(int type_id) +{ + for (GClassInfo *cur_rt = ci_collection; cur_rt < ci_collection_end; ++cur_rt) + { + if (cur_rt->m_type_id == type_id) + { + return cur_rt; + } + } + return NULL; +} + +Geometry::GClassInfo *Geometry::find_class(const char *name, size_t len) +{ + for (GClassInfo *cur_rt = ci_collection; + cur_rt < ci_collection_end; ++cur_rt) + { + if ((cur_rt->m_name[len] == 0) && + (strncmp(cur_rt->m_name, name, len) == 0)) + { + return cur_rt; + } + } + return NULL; +} + +int Geometry::create_from_wkb(const char *data, uint32 data_len) +{ + uint32 geom_type; + + if (data_len < 1+4) + return 1; + data += sizeof(char); + +//FIXME: check byte ordering + geom_type = uint4korr(data); + data += 4; + m_vmt = find_class(geom_type); + if (!m_vmt) return -1; + m_data = data; + m_data_end = data + data_len; + return 0; +} + +int Geometry::create_from_wkt(GTextReadStream *trs, String *wkt, int init_stream) +{ + int name_len; + const char *name = trs->get_next_word(&name_len); + if (!name) + { + trs->set_error_msg("Geometry name expected"); + return -1; + } + if (!(m_vmt = find_class(name, name_len))) + return -1; + if (wkt->reserve(1 + 4, 512)) + return 1; + wkt->q_append((char)wkbNDR); + wkt->q_append((uint32)get_class_info()->m_type_id); + if (trs->get_next_symbol() != '(') + { + trs->set_error_msg("'(' expected"); + return -1; + } + if (init_from_text(trs, wkt)) return 1; + if (trs->get_next_symbol() != ')') + { + trs->set_error_msg("')' expected"); + return -1; + } + if (init_stream) + { + init_from_wkb(wkt->ptr(), wkt->length()); + shift_wkb_header(); + } + return 0; +} + +int Geometry::envelope(String *result) const +{ + MBR mbr; + + get_mbr(&mbr); + + if (result->reserve(1+4*3+sizeof(double)*10)) + return 1; + + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbPolygon); + result->q_append((uint32)1); + result->q_append((uint32)5); + result->q_append(mbr.xmin); + result->q_append(mbr.ymin); + result->q_append(mbr.xmax); + result->q_append(mbr.ymin); + result->q_append(mbr.xmax); + result->q_append(mbr.ymax); + result->q_append(mbr.xmin); + result->q_append(mbr.ymax); + result->q_append(mbr.xmin); + result->q_append(mbr.ymin); + + return 0; +} + +/***************************** Point *******************************/ + +size_t GPoint::get_data_size() const +{ + return POINT_DATA_SIZE; +} + +int GPoint::init_from_text(GTextReadStream *trs, String *wkb) +{ + double x, y; + if (wkb->reserve(sizeof(double)*2)) + return 1; + if (trs->get_next_number(&x)) + return 1; + if (trs->get_next_number(&y)) + return 1; + wkb->q_append(x); + wkb->q_append(y); + + return 0; +} + +int GPoint::get_data_as_text(String *txt) const +{ + double x, y; + if (get_xy(&x, &y)) + return 1; + if (txt->reserve(MAX_DIGITS_IN_DOUBLE * 2 + 1)) + return 1; + txt->qs_append(x); + txt->qs_append(' '); + txt->qs_append(y); + return 0; +} + +int GPoint::get_mbr(MBR *mbr) const +{ + double x, y; + if (get_xy(&x, &y)) + return 1; + mbr->add_xy(x, y); + return 0; +} + +/***************************** LineString *******************************/ + +size_t GLineString::get_data_size() const +{ + uint32 n_points = uint4korr(m_data); + + return 4 + n_points*POINT_DATA_SIZE; +} + +int GLineString::init_from_text(GTextReadStream *trs, String *wkb) +{ + uint32 n_points = 0; + int np_pos = wkb->length(); + GPoint p; + + if (wkb->reserve(4, 512)) + return 1; + + wkb->q_append((uint32)n_points); + + for (;;) + { + if (p.init_from_text(trs, wkb)) + return 1; + ++n_points; + if (trs->get_next_toc_type() == GTextReadStream::comma) + trs->get_next_symbol(); + else break; + } + + if (n_points<2) + { + trs->set_error_msg("Too few points in LINESTRING"); + return 1; + } + + wkb->WriteAtPosition(np_pos, n_points); + + return 0; +} + +int GLineString::get_data_as_text(String *txt) const +{ + uint32 n_points; + const char *data = m_data; + + if (no_data(data, 4)) + return 1; + + n_points = uint4korr(data); + data += 4; + + if (no_data(data, sizeof(double) * 2 * n_points)) + return 1; + + if (txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points)) + return 1; + for (; n_points>0; --n_points) + { + double x, y; + float8get(x, data); + data += sizeof(double); + float8get(y, data); + data += sizeof(double); + txt->qs_append(x); + txt->qs_append(' '); + txt->qs_append(y); + txt->qs_append(','); + } + txt->length(txt->length() - 1); + return 0; +} + +int GLineString::get_mbr(MBR *mbr) const +{ + uint32 n_points; + const char *data = m_data; + + if (no_data(data, 4)) + return 1; + + n_points = uint4korr(data); + data += 4; + + if (no_data(data, sizeof(double) * 2 * n_points)) + return 1; + for (; n_points>0; --n_points) + { + mbr->add_xy((double *)data, (double *)(data + 8)); + data += 8+8; + } + + return 0; +} + +int GLineString::length(double *len) const +{ + uint32 n_points; + double prev_x, prev_y; + const char *data = m_data; + + *len=0; + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + data += 4; + + if (no_data(data, sizeof(double) * 2 * n_points)) + return 1; + + --n_points; + float8get(prev_x, data); + data += 8; + float8get(prev_y, data); + data += 8; + + for (; n_points>0; --n_points) + { + double x, y; + float8get(x, data); + data += 8; + float8get(y, data); + data += 8; + *len+=sqrt(pow(prev_x-x,2)+pow(prev_y-y,2)); + prev_x=x; + prev_y=y; + } + return 0; +} + +int GLineString::is_closed(int *closed) const + +{ + uint32 n_points; + double x1, y1, x2, y2; + + const char *data = m_data; + + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + data += 4; + if (no_data(data, (8+8) * n_points)) + return 1; + float8get(x1, data); + data += 8; + float8get(y1, data); + data += 8 + (n_points-2)*POINT_DATA_SIZE; + float8get(x2, data); + data += 8; + float8get(y2, data); + + *closed=(x1==x2)&&(y1==y2); + + return 0; +} + +int GLineString::num_points(uint32 *n_points) const +{ + *n_points = uint4korr(m_data); + return 0; +} + +int GLineString::start_point(String *result) const +{ + const char *data = m_data + 4; + if (no_data(data, 8+8)) + return 1; + + if (result->reserve(1 + 4 + sizeof(double) * 2)) + return 1; + + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbPoint); + result->q_append((double *)data); + result->q_append((double *)(data + 8)); + + return 0; +} + +int GLineString::end_point(String *result) const +{ + const char *data = m_data; + uint32 n_points; + + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + + data += 4 + (n_points-1)*POINT_DATA_SIZE; + + if (no_data(data, 8+8)) + return 1; + + if (result->reserve(1 + 4 + sizeof(double) * 2)) + return 1; + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbPoint); + result->q_append((double *)data); + result->q_append((double *)(data + 8)); + + return 0; +} + + +int GLineString::point_n(uint32 num, String *result) const +{ + const char *data = m_data; + uint32 n_points; + + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + + if ((uint32)(num-1) >= n_points) // really means (num > n_points || num < 1) + return 1; + + data += 4 + (num - 1)*POINT_DATA_SIZE; + + if (no_data(data, 8+8)) + return 1; + if (result->reserve(1 + 4 + sizeof(double) * 2)) + return 1; + + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbPoint); + result->q_append((double *)data); + result->q_append((double *)(data + 8)); + + return 0; +} + +/***************************** Polygon *******************************/ + +size_t GPolygon::get_data_size() const +{ + uint32 n_linear_rings = 0; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + + n_linear_rings = uint4korr(data); + data += 4; + for (; n_linear_rings>0; --n_linear_rings) + { + if (no_data(data, 4)) + return 1; + data += 4 + uint4korr(data)*POINT_DATA_SIZE; + } + return data - m_data; +} + +int GPolygon::init_from_text(GTextReadStream *trs, String *wkb) +{ + uint32 n_linear_rings = 0; + int lr_pos = wkb->length(); + + if (wkb->reserve(4, 512)) + return 1; + + wkb->q_append((uint32)n_linear_rings); + + for (;;) + { + GLineString ls; + size_t ls_pos=wkb->length(); + if (trs->get_next_symbol() != '(') + { + trs->set_error_msg("'(' expected"); + return 1; + } + if (ls.init_from_text(trs, wkb)) + return 1; + if (trs->get_next_symbol() != ')') + { + trs->set_error_msg("')' expected"); + return 1; + } + ls.init_from_wkb(wkb->ptr()+ls_pos, wkb->length()-ls_pos); + int closed; + ls.is_closed(&closed); + if (!closed) + { + trs->set_error_msg("POLYGON's linear ring isn't closed"); + return 1; + } + ++n_linear_rings; + if (trs->get_next_toc_type() == GTextReadStream::comma) + trs->get_next_symbol(); + else + break; + } + wkb->WriteAtPosition(lr_pos, n_linear_rings); + return 0; +} + +int GPolygon::get_data_as_text(String *txt) const +{ + uint32 n_linear_rings; + const char *data = m_data; + + if (no_data(data, 4)) + return 1; + + n_linear_rings = uint4korr(data); + data += 4; + + for (; n_linear_rings>0; --n_linear_rings) + { + if(no_data(data, 4)) + return 1; + uint32 n_points = uint4korr(data); + data += 4; + if (no_data(data, (8+8) * n_points)) + return 1; + + if (txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points)) + return 1; + txt->qs_append('('); + for (; n_points>0; --n_points) + { + txt->qs_append((double *)data); + txt->qs_append(' '); + txt->qs_append((double *)(data + 8)); + txt->qs_append(','); + + data += 8+8; + } + (*txt)[txt->length()-1] = ')'; + txt->qs_append(','); + } + txt->length(txt->length() - 1); + return 0; +} + +int GPolygon::get_mbr(MBR *mbr) const +{ + uint32 n_linear_rings; + + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_linear_rings = uint4korr(data); + data += 4; + for (; n_linear_rings>0; --n_linear_rings) + { + if (no_data(data, 4)) + return 1; + uint32 n_points = uint4korr(data); + data += 4; + if (no_data(data, (8+8) * n_points)) + return 1; + for (; n_points>0; --n_points) + { + mbr->add_xy((double *)data, (double *)(data + 8)); + data += 8+8; + } + } + return 0; +} + +int GPolygon::area(double *ar) const +{ + uint32 n_linear_rings; + double result = -1.0; + + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_linear_rings = uint4korr(data); + data += 4; + for (; n_linear_rings>0; --n_linear_rings) + { + double prev_x, prev_y; + double lr_area=0; + if (no_data(data, 4)) + return 1; + uint32 n_points = uint4korr(data); + if (no_data(data, (8+8) * n_points)) + return 1; + float8get(prev_x, data+4); + float8get(prev_y, data+(4+8)); + data += (4+8+8); + + --n_points; + for (; n_points>0; --n_points) + { + double x, y; + float8get(x, data); + float8get(y, data + 8); + lr_area+=(prev_x+x)*(prev_y-y); + prev_x=x; + prev_y=y; + data += (8+8); + } + lr_area=fabs(lr_area)/2; + if(result==-1) result=lr_area; + else result-=lr_area; + } + *ar=fabs(result); + return 0; +} + + +int GPolygon::exterior_ring(String *result) const +{ + uint32 n_points; + const char *data = m_data + 4; // skip n_linerings + + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + data += 4; + if (no_data(data, n_points * POINT_DATA_SIZE)) + return 1; + + if (result->reserve(1+4+4+ n_points * POINT_DATA_SIZE)) + return 1; + + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbLineString); + result->q_append(n_points); + result->q_append(data, n_points * POINT_DATA_SIZE); + + return 0; +} + +int GPolygon::num_interior_ring(uint32 *n_int_rings) const +{ + const char *data = m_data; + if (no_data(data, 4)) + return 1; + *n_int_rings = uint4korr(data); + --(*n_int_rings); + + return 0; +} + +int GPolygon::interior_ring_n(uint32 num, String *result) const +{ + const char *data = m_data; + uint32 n_linear_rings; + uint32 n_points; + + if (no_data(data, 4)) + return 1; + + n_linear_rings = uint4korr(data); + data += 4; + if ((num >= n_linear_rings) || (num < 1)) + return -1; + + for (; num > 0; --num) + { + if (no_data(data, 4)) + return 1; + data += 4 + uint4korr(data) * POINT_DATA_SIZE; + } + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + int points_size = n_points * POINT_DATA_SIZE; + data += 4; + if (no_data(data, points_size)) + return 1; + + if (result->reserve(1+4+4+ points_size)) + return 1; + + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbLineString); + result->q_append(n_points); + result->q_append(data, points_size); + + return 0; +} + +int GPolygon::centroid_xy(double *x, double *y) const +{ + uint32 n_linear_rings; + uint32 i; + double res_area, res_cx, res_cy; + const char *data = m_data; + + if (no_data(data, 4)) + return 1; + n_linear_rings = uint4korr(data); + data += 4; + + for(i = 0; i < n_linear_rings; ++i) + { + if (no_data(data, 4)) + return 1; + uint32 n_points = uint4korr(data); + double prev_x, prev_y; + double cur_area = 0; + double cur_cx = 0; + double cur_cy = 0; + + data += 4; + if (no_data(data, (8+8) * n_points)) + return 1; + float8get(prev_x, data); + float8get(prev_y, data+8); + data += (8+8); + + uint32 n = n_points - 1; + for (; n > 0; --n) + { + double x, y; + float8get(x, data); + float8get(y, data + 8); + + cur_area += (prev_x + x) * (prev_y - y); + cur_cx += x; + cur_cy += y; + prev_x = x; + prev_y = y; + data += (8+8); + } + cur_area = fabs(cur_area) / 2; + cur_cx = cur_cx / (n_points - 1); + cur_cy = cur_cy / (n_points - 1); + if(i) + { + double d_area = res_area - cur_area; + if (d_area <= 0) + return 1; + res_cx = (res_area * res_cx - cur_area * cur_cx) / d_area; + res_cy = (res_area * res_cy - cur_area * cur_cy) / d_area; + } + else + { + res_area = cur_area; + res_cx = cur_cx; + res_cy = cur_cy; + } + } + + *x = res_cx; + *y = res_cy; + + return 0; +} + +int GPolygon::centroid(String *result) const +{ + double x, y; + + this->centroid_xy(&x, &y); + if (result->reserve(1 + 4 + sizeof(double) * 2)) + return 1; + + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbPoint); + result->q_append(x); + result->q_append(y); + + return 0; +} + + +/***************************** MultiPoint *******************************/ + +size_t GMultiPoint::get_data_size() const +{ + return 4 + uint4korr(m_data)*(POINT_DATA_SIZE + WKB_HEADER_SIZE); +} + +int GMultiPoint::init_from_text(GTextReadStream *trs, String *wkb) +{ + uint32 n_points = 0; + int np_pos = wkb->length(); + GPoint p; + + if (wkb->reserve(4, 512)) + return 1; + wkb->q_append((uint32)n_points); + + for (;;) + { + if (wkb->reserve(1+4, 512)) + return 1; + wkb->q_append((char)wkbNDR); + wkb->q_append((uint32)wkbPoint); + if (p.init_from_text(trs, wkb)) + return 1; + ++n_points; + if (trs->get_next_toc_type() == GTextReadStream::comma) + trs->get_next_symbol(); + else + break; + } + wkb->WriteAtPosition(np_pos, n_points); + + return 0; +} + +int GMultiPoint::get_data_as_text(String *txt) const +{ + uint32 n_points; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + + n_points = uint4korr(data); + data += 4; + if (no_data(data, n_points * (8+8+WKB_HEADER_SIZE))) + return 1; + + if (txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points)) + return 1; + + for (; n_points>0; --n_points) + { + txt->qs_append((double *)(data + WKB_HEADER_SIZE)); + txt->qs_append(' '); + txt->qs_append((double *)(data + (8 + WKB_HEADER_SIZE))); + txt->qs_append(','); + data += 8+8+WKB_HEADER_SIZE; + } + txt->length(txt->length()-1); + return 0; +} + +int GMultiPoint::get_mbr(MBR *mbr) const +{ + uint32 n_points; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_points = uint4korr(data); + data += 4; + if (no_data(data, n_points * (8+8+WKB_HEADER_SIZE))) + return 1; + for (; n_points>0; --n_points) + { + mbr->add_xy((double *)(data + WKB_HEADER_SIZE), + (double *)(data + 8 + WKB_HEADER_SIZE)); + data += (8+8+WKB_HEADER_SIZE); + } + return 0; +} + +/***************************** MultiLineString *******************************/ + +size_t GMultiLineString::get_data_size() const +{ + uint32 n_line_strings = 0; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_line_strings = uint4korr(data); + data += 4; + + for (; n_line_strings>0; --n_line_strings) + { + if (no_data(data, WKB_HEADER_SIZE + 4)) + return 1; + data += WKB_HEADER_SIZE + 4 + uint4korr(data + WKB_HEADER_SIZE) * POINT_DATA_SIZE; + } + return data - m_data; +} + +int GMultiLineString::init_from_text(GTextReadStream *trs, String *wkb) +{ + uint32 n_line_strings = 0; + int ls_pos = wkb->length(); + + if (wkb->reserve(4, 512)) + return 1; + + wkb->q_append((uint32)n_line_strings); + + for (;;) + { + GLineString ls; + + if (wkb->reserve(1+4, 512)) + return 1; + wkb->q_append((char)wkbNDR); + wkb->q_append((uint32)wkbLineString); + + if (trs->get_next_symbol() != '(') + { + trs->set_error_msg("'(' expected"); + return 1; + } + if (ls.init_from_text(trs, wkb)) + return 1; + + if (trs->get_next_symbol() != ')') + { + trs->set_error_msg("')' expected"); + return 1; + } + ++n_line_strings; + if (trs->get_next_toc_type() == GTextReadStream::comma) + trs->get_next_symbol(); + else + break; + } + wkb->WriteAtPosition(ls_pos, n_line_strings); + + return 0; +} + +int GMultiLineString::get_data_as_text(String *txt) const +{ + uint32 n_line_strings; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_line_strings = uint4korr(data); + data += 4; + for (; n_line_strings>0; --n_line_strings) + { + if (no_data(data, (WKB_HEADER_SIZE + 4))) + return 1; + uint32 n_points = uint4korr(data + WKB_HEADER_SIZE); + data += WKB_HEADER_SIZE + 4; + if (no_data(data, n_points * (8+8))) + return 1; + + if (txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points)) + return 1; + txt->qs_append('('); + for (; n_points>0; --n_points) + { + txt->qs_append((double *)data); + txt->qs_append(' '); + txt->qs_append((double *)(data + 8)); + txt->qs_append(','); + data += 8+8; + } + (*txt)[txt->length()-1] = ')'; + txt->qs_append(','); + } + txt->length(txt->length() - 1); + return 0; +} + +int GMultiLineString::get_mbr(MBR *mbr) const +{ + uint32 n_line_strings; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_line_strings = uint4korr(data); + data += 4; + + for (; n_line_strings>0; --n_line_strings) + { + if (no_data(data, WKB_HEADER_SIZE + 4)) + return 1; + uint32 n_points = uint4korr(data + WKB_HEADER_SIZE); + data += 4+WKB_HEADER_SIZE; + if (no_data(data, (8+8)*n_points)) + return 1; + + for (; n_points>0; --n_points) + { + mbr->add_xy((double *)data, (double *)(data + 8)); + data += 8+8; + } + } + return 0; +} + +int GMultiLineString::length(double *len) const +{ + uint32 n_line_strings; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_line_strings = uint4korr(data); + data += 4; + *len=0; + for (; n_line_strings>0; --n_line_strings) + { + double ls_len; + GLineString ls; + data += WKB_HEADER_SIZE; + ls.init_from_wkb(data, m_data_end - data); + if (ls.length(&ls_len)) + return 1; + *len+=ls_len; + data += ls.get_data_size(); + } + return 0; +} + +int GMultiLineString::is_closed(int *closed) const +{ + uint32 n_line_strings; + const char *data = m_data; + if (no_data(data, 1)) + return 1; + n_line_strings = uint4korr(data); + data += 4 + WKB_HEADER_SIZE; + for (; n_line_strings>0; --n_line_strings) + { + GLineString ls; + ls.init_from_wkb(data, m_data_end - data); + if (ls.is_closed(closed)) + return 1; + if (!*closed) + return 0; + data += ls.get_data_size() + WKB_HEADER_SIZE; + } + return 0; +} + +/***************************** MultiPolygon *******************************/ + +size_t GMultiPolygon::get_data_size() const +{ + uint32 n_polygons; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_polygons = uint4korr(data); + data += 4; + + for (; n_polygons>0; --n_polygons) + { + if (no_data(data, 4 + WKB_HEADER_SIZE)) + return 1; + uint32 n_linear_rings = uint4korr(data + WKB_HEADER_SIZE); + data += 4 + WKB_HEADER_SIZE; + + for (; n_linear_rings > 0; --n_linear_rings) + { + data += 4 + uint4korr(data) * POINT_DATA_SIZE; + } + } + return data - m_data; +} + +int GMultiPolygon::init_from_text(GTextReadStream *trs, String *wkb) +{ + uint32 n_polygons = 0; + int np_pos = wkb->length(); + GPolygon p; + + if (wkb->reserve(4, 512)) + return 1; + + wkb->q_append((uint32)n_polygons); + + for (;;) + { + if (wkb->reserve(1+4, 512)) + return 1; + wkb->q_append((char)wkbNDR); + wkb->q_append((uint32)wkbPolygon); + + if (trs->get_next_symbol() != '(') + { + trs->set_error_msg("'(' expected"); + return 1; + } + if (p.init_from_text(trs, wkb)) + return 1; + if (trs->get_next_symbol() != ')') + { + trs->set_error_msg("')' expected"); + return 1; + } + ++n_polygons; + if (trs->get_next_toc_type() == GTextReadStream::comma) + trs->get_next_symbol(); + else + break; + } + wkb->WriteAtPosition(np_pos, n_polygons); + return 0; +} + +int GMultiPolygon::get_data_as_text(String *txt) const +{ + uint32 n_polygons; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_polygons = uint4korr(data); + data += 4; + + for (; n_polygons>0; --n_polygons) + { + if (no_data(data, 4 + WKB_HEADER_SIZE)) + return 1; + data += WKB_HEADER_SIZE; + uint32 n_linear_rings = uint4korr(data); + data += 4; + + if (txt->reserve(1, 512)) + return 1; + txt->q_append('('); + for (; n_linear_rings>0; --n_linear_rings) + { + if (no_data(data, 4)) + return 1; + uint32 n_points = uint4korr(data); + data += 4; + if (no_data(data, (8+8)*n_points)) return 1; + + if (txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points, + 512)) return 1; + txt->qs_append('('); + for (; n_points>0; --n_points) + { + txt->qs_append((double *)data); + txt->qs_append(' '); + txt->qs_append((double *)(data + 8)); + txt->qs_append(','); + data += 8+8; + } + (*txt)[txt->length()-1] = ')'; + txt->qs_append(','); + } + (*txt)[txt->length()-1] = ')'; + txt->qs_append(','); + } + txt->length(txt->length() - 1); + return 0; +} + +int GMultiPolygon::get_mbr(MBR *mbr) const +{ + uint32 n_polygons; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_polygons = uint4korr(data); + data += 4; + + for (; n_polygons>0; --n_polygons) + { + if (no_data(data, 4+WKB_HEADER_SIZE)) + return 1; + uint32 n_linear_rings = uint4korr(data + WKB_HEADER_SIZE); + data += WKB_HEADER_SIZE + 4; + + for (; n_linear_rings>0; --n_linear_rings) + { + if (no_data(data, 4)) + return 1; + uint32 n_points = uint4korr(data); + data += 4; + if (no_data(data, (8+8)*n_points)) + return 1; + + for (; n_points>0; --n_points) + { + mbr->add_xy((double *)data, (double *)(data + 8)); + data += 8+8; + } + } + } + return 0; +} + + +int GMultiPolygon::area(double *ar) const +{ + uint32 n_polygons; + const char *data = m_data; + double result = 0; + if (no_data(data, 4)) + return 1; + n_polygons = uint4korr(data); + data += 4; + + for (; n_polygons>0; --n_polygons) + { + double p_area; + + GPolygon p; + data += WKB_HEADER_SIZE; + p.init_from_wkb(data, m_data_end - data); + if (p.area(&p_area)) + return 1; + result += p_area; + data += p.get_data_size(); + } + *ar = result; + return 0; +} + +int GMultiPolygon::centroid(String *result) const +{ + uint32 n_polygons; + uint i; + GPolygon p; + double res_area, res_cx, res_cy; + double cur_area, cur_cx, cur_cy; + + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_polygons = uint4korr(data); + data += 4; + + for (i = 0; i < n_polygons; ++i) + { + data += WKB_HEADER_SIZE; + p.init_from_wkb(data, m_data_end - data); + if (p.area(&cur_area)) + return 1; + + if (p.centroid_xy(&cur_cx, &cur_cy)) + return 1; + + if(i) + { + double sum_area = res_area + cur_area; + res_cx = (res_area * res_cx + cur_area * cur_cx) / sum_area; + res_cy = (res_area * res_cy + cur_area * cur_cy) / sum_area; + } + else + { + res_area = cur_area; + res_cx = cur_cx; + res_cy = cur_cy; + } + + data += p.get_data_size(); + } + + if (result->reserve(1 + 4 + sizeof(double) * 2)) + return 1; + result->q_append((char)wkbNDR); + result->q_append((uint32)wkbPoint); + result->q_append(res_cx); + result->q_append(res_cy); + + return 0; +} + +/***************************** GeometryCollection *******************************/ + +size_t GGeometryCollection::get_data_size() const +{ + uint32 n_objects; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_objects = uint4korr(data); + data += 4; + + for (; n_objects>0; --n_objects) + { + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + uint32 wkb_type = uint4korr(data + sizeof(char)); + data += WKB_HEADER_SIZE; + + Geometry geom; + + if (geom.init(wkb_type)) + return 0; + + geom.init_from_wkb(data, m_data_end - data); + size_t object_size=geom.get_data_size(); + data += object_size; + } + return data - m_data; +} + +int GGeometryCollection::init_from_text(GTextReadStream *trs, String *wkb) +{ + uint32 n_objects = 0; + int no_pos = wkb->length(); + Geometry g; + + if (wkb->reserve(4, 512)) + return 1; + wkb->q_append((uint32)n_objects); + + for (;;) + { + if (g.create_from_wkt(trs, wkb)) + return 1; + + if (g.get_class_info()->m_type_id==wkbGeometryCollection) + { + trs->set_error_msg("Unexpected GEOMETRYCOLLECTION"); + return 1; + } + ++n_objects; + if (trs->get_next_toc_type() == GTextReadStream::comma) + trs->get_next_symbol(); + else break; + } + wkb->WriteAtPosition(no_pos, n_objects); + + return 0; +} + +int GGeometryCollection::get_data_as_text(String *txt) const +{ + uint32 n_objects; + const char *data = m_data; + Geometry geom; + if (no_data(data, 4)) + return 1; + n_objects = uint4korr(data); + data += 4; + + for (; n_objects>0; --n_objects) + { + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + uint32 wkb_type = uint4korr(data + sizeof(char)); + data += WKB_HEADER_SIZE; + + if (geom.init(wkb_type)) + return 1; + geom.init_from_wkb(data, m_data_end - data); + if (geom.as_wkt(txt)) + return 1; + data += geom.get_data_size(); + txt->reserve(1, 512); + txt->q_append(','); + } + txt->length(txt->length() - 1); + return 0; +} + +int GGeometryCollection::get_mbr(MBR *mbr) const +{ + uint32 n_objects; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_objects = uint4korr(data); + data += 4; + for (; n_objects>0; --n_objects) + { + if(no_data(data, WKB_HEADER_SIZE)) + return 1; + uint32 wkb_type = uint4korr(data + sizeof(char)); + data += WKB_HEADER_SIZE; + Geometry geom; + + if (geom.init(wkb_type)) + return 1; + geom.init_from_wkb(data, m_data_end - data); + geom.get_mbr(mbr); + data += geom.get_data_size(); + } + return 0; +} + +int GGeometryCollection::num_geometries(uint32 *num) const +{ + *num = uint4korr(m_data); + return 0; +} + +int GGeometryCollection::geometry_n(uint32 num, String *result) const +{ + const char *data = m_data; + uint32 n_objects; + if (no_data(data, 4)) + return 1; + n_objects = uint4korr(data); + data += 4; + + if ((num > n_objects) || (num < 1)) + { + return -1; + } + for (; num > 0; --num) + { + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + uint32 wkb_type = uint4korr(data + sizeof(char)); + data += WKB_HEADER_SIZE; + + Geometry geom; + if (geom.init(wkb_type)) + return 1; + geom.init_from_wkb(data, m_data_end - data); + if (num == 1) + { + if (result->reserve(1+4+geom.get_data_size())) + return 1; + result->q_append((char)wkbNDR); + result->q_append((uint32)wkb_type); + result->q_append(data, geom.get_data_size()); + break; + } + else + { + data += geom.get_data_size(); + } + } + return 0; +} + +int GGeometryCollection::dimension(uint32 *dim) const +{ + uint32 n_objects; + *dim = 0; + const char *data = m_data; + if (no_data(data, 4)) + return 1; + n_objects = uint4korr(data); + data += 4; + + for (; n_objects > 0; --n_objects) + { + if (no_data(data, WKB_HEADER_SIZE)) + return 1; + uint32 wkb_type = uint4korr(data + sizeof(char)); + data += WKB_HEADER_SIZE; + + uint32 d; + + Geometry geom; + if (geom.init(wkb_type)) + return 1; + geom.init_from_wkb(data, m_data_end - data); + if (geom.dimension(&d)) + return 1; + + if (d > *dim) + *dim = d; + data += geom.get_data_size(); + } + return 0; +} + +/***************************** /objects *******************************/ |