#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)/sizeof(ci_collection[0]); /***************************** 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) && (default_charset_info->strncasecmp(default_charset_info, 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(data, 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(data, 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; LINT_INIT(res_area); LINT_INIT(res_cx); LINT_INIT(res_cy); 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(data + WKB_HEADER_SIZE, 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(data, 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(data, 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; LINT_INIT(res_area); LINT_INIT(res_cx); LINT_INIT(res_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 *******************************/