mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 12:32:27 +01:00
12f364ece7
to string conversions and vice versa" Initial import of the dtoa.c code and custom wrappers around it to allow its usage from the server code. Conversion of FLOAT/DOUBLE values to DECIMAL ones or strings and vice versa has been significantly reworked. As the new algoritms are more precise than the older ones, results of such conversions may not always match those obtained from older server versions. This in turn may break compatibility for some applications. This patch also fixes the following bugs: - bug #12860 "Difference in zero padding of exponent between Unix and Windows" - bug #21497 "DOUBLE truncated to unusable value" - bug #26788 "mysqld (debug) aborts when inserting specific numbers into char fields" - bug #24541 "Data truncated..." on decimal type columns without any good reason"
1938 lines
42 KiB
C++
1938 lines
42 KiB
C++
/* Copyright (C) 2004 MySQL 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
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
#include "mysql_priv.h"
|
|
|
|
#ifdef HAVE_SPATIAL
|
|
|
|
#define MAX_DIGITS_IN_DOUBLE MY_GCVT_MAX_FIELD_WIDTH
|
|
|
|
/***************************** Gis_class_info *******************************/
|
|
|
|
String Geometry::bad_geometry_data("Bad object", &my_charset_bin);
|
|
|
|
Geometry::Class_info *Geometry::ci_collection[Geometry::wkb_last+1]=
|
|
{
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
|
};
|
|
|
|
static Geometry::Class_info **ci_collection_end=
|
|
Geometry::ci_collection+Geometry::wkb_last + 1;
|
|
|
|
Geometry::Class_info::Class_info(const char *name, int type_id,
|
|
void(*create_func)(void *)):
|
|
m_type_id(type_id), m_create_func(create_func)
|
|
{
|
|
m_name.str= (char *) name;
|
|
m_name.length= strlen(name);
|
|
|
|
ci_collection[type_id]= this;
|
|
}
|
|
|
|
static void create_point(void *buffer)
|
|
{
|
|
new(buffer) Gis_point;
|
|
}
|
|
|
|
static void create_linestring(void *buffer)
|
|
{
|
|
new(buffer) Gis_line_string;
|
|
}
|
|
|
|
static void create_polygon(void *buffer)
|
|
{
|
|
new(buffer) Gis_polygon;
|
|
}
|
|
|
|
static void create_multipoint(void *buffer)
|
|
{
|
|
new(buffer) Gis_multi_point;
|
|
}
|
|
|
|
static void create_multipolygon(void *buffer)
|
|
{
|
|
new(buffer) Gis_multi_polygon;
|
|
}
|
|
|
|
static void create_multilinestring(void *buffer)
|
|
{
|
|
new(buffer) Gis_multi_line_string;
|
|
}
|
|
|
|
static void create_geometrycollection(void *buffer)
|
|
{
|
|
new(buffer) Gis_geometry_collection;
|
|
}
|
|
|
|
|
|
|
|
static Geometry::Class_info point_class("POINT",
|
|
Geometry::wkb_point, create_point);
|
|
|
|
static Geometry::Class_info linestring_class("LINESTRING",
|
|
Geometry::wkb_linestring,
|
|
create_linestring);
|
|
static Geometry::Class_info polygon_class("POLYGON",
|
|
Geometry::wkb_polygon,
|
|
create_polygon);
|
|
static Geometry::Class_info multipoint_class("MULTIPOINT",
|
|
Geometry::wkb_multipoint,
|
|
create_multipoint);
|
|
static Geometry::Class_info
|
|
multilinestring_class("MULTILINESTRING",
|
|
Geometry::wkb_multilinestring, create_multilinestring);
|
|
static Geometry::Class_info multipolygon_class("MULTIPOLYGON",
|
|
Geometry::wkb_multipolygon,
|
|
create_multipolygon);
|
|
static Geometry::Class_info
|
|
geometrycollection_class("GEOMETRYCOLLECTION",Geometry::wkb_geometrycollection,
|
|
create_geometrycollection);
|
|
|
|
static void get_point(double *x, double *y, const char *data)
|
|
{
|
|
float8get(*x, data);
|
|
float8get(*y, data + SIZEOF_STORED_DOUBLE);
|
|
}
|
|
|
|
/***************************** Geometry *******************************/
|
|
|
|
Geometry::Class_info *Geometry::find_class(const char *name, uint32 len)
|
|
{
|
|
for (Class_info **cur_rt= ci_collection;
|
|
cur_rt < ci_collection_end; cur_rt++)
|
|
{
|
|
if (*cur_rt &&
|
|
((*cur_rt)->m_name.length == len) &&
|
|
(my_strnncoll(&my_charset_latin1,
|
|
(const uchar*) (*cur_rt)->m_name.str, len,
|
|
(const uchar*) name, len) == 0))
|
|
return *cur_rt;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
Geometry *Geometry::construct(Geometry_buffer *buffer,
|
|
const char *data, uint32 data_len)
|
|
{
|
|
uint32 geom_type;
|
|
Geometry *result;
|
|
char byte_order;
|
|
|
|
if (data_len < SRID_SIZE + WKB_HEADER_SIZE) // < 4 + (1 + 4)
|
|
return NULL;
|
|
byte_order= data[SRID_SIZE];
|
|
geom_type= uint4korr(data + SRID_SIZE + 1);
|
|
if (!(result= create_by_typeid(buffer, (int) geom_type)))
|
|
return NULL;
|
|
result->m_data= data+ SRID_SIZE + WKB_HEADER_SIZE;
|
|
result->m_data_end= data + data_len;
|
|
return result;
|
|
}
|
|
|
|
|
|
Geometry *Geometry::create_from_wkt(Geometry_buffer *buffer,
|
|
Gis_read_stream *trs, String *wkt,
|
|
bool init_stream)
|
|
{
|
|
LEX_STRING name;
|
|
Class_info *ci;
|
|
|
|
if (trs->get_next_word(&name))
|
|
{
|
|
trs->set_error_msg("Geometry name expected");
|
|
return NULL;
|
|
}
|
|
if (!(ci= find_class(name.str, name.length)) ||
|
|
wkt->reserve(1 + 4, 512))
|
|
return NULL;
|
|
(*ci->m_create_func)((void *)buffer);
|
|
Geometry *result= (Geometry *)buffer;
|
|
|
|
wkt->q_append((char) wkb_ndr);
|
|
wkt->q_append((uint32) result->get_class_info()->m_type_id);
|
|
if (trs->check_next_symbol('(') ||
|
|
result->init_from_wkt(trs, wkt) ||
|
|
trs->check_next_symbol(')'))
|
|
return NULL;
|
|
if (init_stream)
|
|
{
|
|
result->set_data_ptr(wkt->ptr(), wkt->length());
|
|
result->shift_wkb_header();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
static double wkb_get_double(const char *ptr, Geometry::wkbByteOrder bo)
|
|
{
|
|
double res;
|
|
if (bo != Geometry::wkb_xdr)
|
|
{
|
|
float8get(res, ptr);
|
|
}
|
|
else
|
|
{
|
|
char inv_array[8];
|
|
inv_array[0]= ptr[7];
|
|
inv_array[1]= ptr[6];
|
|
inv_array[2]= ptr[5];
|
|
inv_array[3]= ptr[4];
|
|
inv_array[4]= ptr[3];
|
|
inv_array[5]= ptr[2];
|
|
inv_array[6]= ptr[1];
|
|
inv_array[7]= ptr[0];
|
|
float8get(res, inv_array);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
static uint32 wkb_get_uint(const char *ptr, Geometry::wkbByteOrder bo)
|
|
{
|
|
if (bo != Geometry::wkb_xdr)
|
|
return uint4korr(ptr);
|
|
/* else */
|
|
{
|
|
char inv_array[4];
|
|
inv_array[0]= ptr[3];
|
|
inv_array[1]= ptr[2];
|
|
inv_array[2]= ptr[1];
|
|
inv_array[3]= ptr[0];
|
|
return uint4korr(inv_array);
|
|
}
|
|
}
|
|
|
|
|
|
Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer,
|
|
const char *wkb, uint32 len, String *res)
|
|
{
|
|
uint32 geom_type;
|
|
Geometry *geom;
|
|
|
|
if (len < WKB_HEADER_SIZE)
|
|
return NULL;
|
|
geom_type= wkb_get_uint(wkb+1, (wkbByteOrder)wkb[0]);
|
|
if (!(geom= create_by_typeid(buffer, (int) geom_type)) ||
|
|
res->reserve(WKB_HEADER_SIZE, 512))
|
|
return NULL;
|
|
|
|
res->q_append((char) wkb_ndr);
|
|
res->q_append(geom_type);
|
|
|
|
return geom->init_from_wkb(wkb + WKB_HEADER_SIZE, len - WKB_HEADER_SIZE,
|
|
(wkbByteOrder) wkb[0], res) ? geom : NULL;
|
|
}
|
|
|
|
|
|
bool Geometry::envelope(String *result) const
|
|
{
|
|
MBR mbr;
|
|
const char *end;
|
|
|
|
if (get_mbr(&mbr, &end) || result->reserve(1+4*3+SIZEOF_STORED_DOUBLE*10))
|
|
return 1;
|
|
|
|
result->q_append((char) wkb_ndr);
|
|
result->q_append((uint32) wkb_polygon);
|
|
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;
|
|
}
|
|
|
|
|
|
/*
|
|
Create a point from data.
|
|
|
|
SYNPOSIS
|
|
create_point()
|
|
result Put result here
|
|
data Data for point is here.
|
|
|
|
RETURN
|
|
0 ok
|
|
1 Can't reallocate 'result'
|
|
*/
|
|
|
|
bool Geometry::create_point(String *result, const char *data) const
|
|
{
|
|
if (no_data(data, SIZEOF_STORED_DOUBLE * 2) ||
|
|
result->reserve(1 + 4 + SIZEOF_STORED_DOUBLE * 2))
|
|
return 1;
|
|
result->q_append((char) wkb_ndr);
|
|
result->q_append((uint32) wkb_point);
|
|
/* Copy two double in same format */
|
|
result->q_append(data, SIZEOF_STORED_DOUBLE*2);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Create a point from coordinates.
|
|
|
|
SYNPOSIS
|
|
create_point()
|
|
result Put result here
|
|
x x coordinate for point
|
|
y y coordinate for point
|
|
|
|
RETURN
|
|
0 ok
|
|
1 Can't reallocate 'result'
|
|
*/
|
|
|
|
bool Geometry::create_point(String *result, double x, double y) const
|
|
{
|
|
if (result->reserve(1 + 4 + SIZEOF_STORED_DOUBLE * 2))
|
|
return 1;
|
|
|
|
result->q_append((char) wkb_ndr);
|
|
result->q_append((uint32) wkb_point);
|
|
result->q_append(x);
|
|
result->q_append(y);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Append N points from packed format to text
|
|
|
|
SYNOPSIS
|
|
append_points()
|
|
txt Append points here
|
|
n_points Number of points
|
|
data Packed data
|
|
offset Offset between points
|
|
|
|
RETURN
|
|
# end of data
|
|
*/
|
|
|
|
const char *Geometry::append_points(String *txt, uint32 n_points,
|
|
const char *data, uint32 offset) const
|
|
{
|
|
while (n_points--)
|
|
{
|
|
double x,y;
|
|
data+= offset;
|
|
get_point(&x, &y, data);
|
|
data+= SIZEOF_STORED_DOUBLE * 2;
|
|
txt->qs_append(x);
|
|
txt->qs_append(' ');
|
|
txt->qs_append(y);
|
|
txt->qs_append(',');
|
|
}
|
|
return data;
|
|
}
|
|
|
|
|
|
/*
|
|
Get most bounding rectangle (mbr) for X points
|
|
|
|
SYNOPSIS
|
|
get_mbr_for_points()
|
|
mbr MBR (store rectangle here)
|
|
points Number of points
|
|
data Packed data
|
|
offset Offset between points
|
|
|
|
RETURN
|
|
0 Wrong data
|
|
# end of data
|
|
*/
|
|
|
|
const char *Geometry::get_mbr_for_points(MBR *mbr, const char *data,
|
|
uint offset) const
|
|
{
|
|
uint32 points;
|
|
/* read number of points */
|
|
if (no_data(data, 4))
|
|
return 0;
|
|
points= uint4korr(data);
|
|
data+= 4;
|
|
|
|
if (no_data(data, (SIZEOF_STORED_DOUBLE * 2 + offset) * points))
|
|
return 0;
|
|
|
|
/* Calculate MBR for points */
|
|
while (points--)
|
|
{
|
|
data+= offset;
|
|
mbr->add_xy(data, data + SIZEOF_STORED_DOUBLE);
|
|
data+= SIZEOF_STORED_DOUBLE * 2;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
|
|
/***************************** Point *******************************/
|
|
|
|
uint32 Gis_point::get_data_size() const
|
|
{
|
|
return POINT_DATA_SIZE;
|
|
}
|
|
|
|
|
|
bool Gis_point::init_from_wkt(Gis_read_stream *trs, String *wkb)
|
|
{
|
|
double x, y;
|
|
if (trs->get_next_number(&x) || trs->get_next_number(&y) ||
|
|
wkb->reserve(SIZEOF_STORED_DOUBLE * 2))
|
|
return 1;
|
|
wkb->q_append(x);
|
|
wkb->q_append(y);
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint Gis_point::init_from_wkb(const char *wkb, uint len,
|
|
wkbByteOrder bo, String *res)
|
|
{
|
|
double x, y;
|
|
if (len < POINT_DATA_SIZE || res->reserve(POINT_DATA_SIZE))
|
|
return 0;
|
|
x= wkb_get_double(wkb, bo);
|
|
y= wkb_get_double(wkb + SIZEOF_STORED_DOUBLE, bo);
|
|
res->q_append(x);
|
|
res->q_append(y);
|
|
return POINT_DATA_SIZE;
|
|
}
|
|
|
|
|
|
bool Gis_point::get_data_as_wkt(String *txt, const char **end) 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);
|
|
*end= m_data+ POINT_DATA_SIZE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Gis_point::get_mbr(MBR *mbr, const char **end) const
|
|
{
|
|
double x, y;
|
|
if (get_xy(&x, &y))
|
|
return 1;
|
|
mbr->add_xy(x, y);
|
|
*end= m_data+ POINT_DATA_SIZE;
|
|
return 0;
|
|
}
|
|
|
|
const Geometry::Class_info *Gis_point::get_class_info() const
|
|
{
|
|
return &point_class;
|
|
}
|
|
|
|
|
|
/***************************** LineString *******************************/
|
|
|
|
uint32 Gis_line_string::get_data_size() const
|
|
{
|
|
if (no_data(m_data, 4))
|
|
return GET_SIZE_ERROR;
|
|
return 4 + uint4korr(m_data) * POINT_DATA_SIZE;
|
|
}
|
|
|
|
|
|
bool Gis_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb)
|
|
{
|
|
uint32 n_points= 0;
|
|
uint32 np_pos= wkb->length();
|
|
Gis_point p;
|
|
|
|
if (wkb->reserve(4, 512))
|
|
return 1;
|
|
wkb->length(wkb->length()+4); // Reserve space for points
|
|
|
|
for (;;)
|
|
{
|
|
if (p.init_from_wkt(trs, wkb))
|
|
return 1;
|
|
n_points++;
|
|
if (trs->skip_char(',')) // Didn't find ','
|
|
break;
|
|
}
|
|
if (n_points < 1)
|
|
{
|
|
trs->set_error_msg("Too few points in LINESTRING");
|
|
return 1;
|
|
}
|
|
wkb->write_at_position(np_pos, n_points);
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint Gis_line_string::init_from_wkb(const char *wkb, uint len,
|
|
wkbByteOrder bo, String *res)
|
|
{
|
|
uint32 n_points, proper_length;
|
|
const char *wkb_end;
|
|
Gis_point p;
|
|
|
|
if (len < 4)
|
|
return 0;
|
|
n_points= wkb_get_uint(wkb, bo);
|
|
proper_length= 4 + n_points * POINT_DATA_SIZE;
|
|
|
|
if (len < proper_length || res->reserve(proper_length))
|
|
return 0;
|
|
|
|
res->q_append(n_points);
|
|
wkb_end= wkb + proper_length;
|
|
for (wkb+= 4; wkb<wkb_end; wkb+= POINT_DATA_SIZE)
|
|
{
|
|
if (!p.init_from_wkb(wkb, POINT_DATA_SIZE, bo, res))
|
|
return 0;
|
|
}
|
|
|
|
return proper_length;
|
|
}
|
|
|
|
|
|
bool Gis_line_string::get_data_as_wkt(String *txt, const char **end) const
|
|
{
|
|
uint32 n_points;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_points= uint4korr(data);
|
|
data += 4;
|
|
|
|
if (n_points < 1 ||
|
|
no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points) ||
|
|
txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points))
|
|
return 1;
|
|
|
|
while (n_points--)
|
|
{
|
|
double x, y;
|
|
get_point(&x, &y, data);
|
|
data+= SIZEOF_STORED_DOUBLE * 2;
|
|
txt->qs_append(x);
|
|
txt->qs_append(' ');
|
|
txt->qs_append(y);
|
|
txt->qs_append(',');
|
|
}
|
|
txt->length(txt->length() - 1); // Remove end ','
|
|
*end= data;
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Gis_line_string::get_mbr(MBR *mbr, const char **end) const
|
|
{
|
|
return (*end=get_mbr_for_points(mbr, m_data, 0)) == 0;
|
|
}
|
|
|
|
|
|
int Gis_line_string::geom_length(double *len) const
|
|
{
|
|
uint32 n_points;
|
|
double prev_x, prev_y;
|
|
const char *data= m_data;
|
|
|
|
*len= 0; // In case of errors
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_points= uint4korr(data);
|
|
data+= 4;
|
|
if (n_points < 1 || no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points))
|
|
return 1;
|
|
|
|
get_point(&prev_x, &prev_y, data);
|
|
data+= SIZEOF_STORED_DOUBLE*2;
|
|
|
|
while (--n_points)
|
|
{
|
|
double x, y;
|
|
get_point(&x, &y, data);
|
|
data+= SIZEOF_STORED_DOUBLE * 2;
|
|
*len+= sqrt(pow(prev_x-x,2)+pow(prev_y-y,2));
|
|
prev_x= x;
|
|
prev_y= y;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_line_string::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);
|
|
if (n_points == 1)
|
|
{
|
|
*closed=1;
|
|
return 0;
|
|
}
|
|
data+= 4;
|
|
if (no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points))
|
|
return 1;
|
|
|
|
/* Get first point */
|
|
get_point(&x1, &y1, data);
|
|
|
|
/* get last point */
|
|
data+= SIZEOF_STORED_DOUBLE*2 + (n_points-2)*POINT_DATA_SIZE;
|
|
get_point(&x2, &y2, data);
|
|
|
|
*closed= (x1==x2) && (y1==y2);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_line_string::num_points(uint32 *n_points) const
|
|
{
|
|
*n_points= uint4korr(m_data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_line_string::start_point(String *result) const
|
|
{
|
|
/* +4 is for skipping over number of points */
|
|
return create_point(result, m_data + 4);
|
|
}
|
|
|
|
|
|
int Gis_line_string::end_point(String *result) const
|
|
{
|
|
uint32 n_points;
|
|
if (no_data(m_data, 4))
|
|
return 1;
|
|
n_points= uint4korr(m_data);
|
|
return create_point(result, m_data + 4 + (n_points - 1) * POINT_DATA_SIZE);
|
|
}
|
|
|
|
|
|
int Gis_line_string::point_n(uint32 num, String *result) const
|
|
{
|
|
uint32 n_points;
|
|
if (no_data(m_data, 4))
|
|
return 1;
|
|
n_points= uint4korr(m_data);
|
|
if ((uint32) (num - 1) >= n_points) // means (num > n_points || num < 1)
|
|
return 1;
|
|
|
|
return create_point(result, m_data + 4 + (num - 1) * POINT_DATA_SIZE);
|
|
}
|
|
|
|
const Geometry::Class_info *Gis_line_string::get_class_info() const
|
|
{
|
|
return &linestring_class;
|
|
}
|
|
|
|
|
|
/***************************** Polygon *******************************/
|
|
|
|
uint32 Gis_polygon::get_data_size() const
|
|
{
|
|
uint32 n_linear_rings;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4))
|
|
return GET_SIZE_ERROR;
|
|
n_linear_rings= uint4korr(data);
|
|
data+= 4;
|
|
|
|
while (n_linear_rings--)
|
|
{
|
|
if (no_data(data, 4))
|
|
return GET_SIZE_ERROR;
|
|
data+= 4 + uint4korr(data)*POINT_DATA_SIZE;
|
|
}
|
|
return (uint32) (data - m_data);
|
|
}
|
|
|
|
|
|
bool Gis_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb)
|
|
{
|
|
uint32 n_linear_rings= 0;
|
|
uint32 lr_pos= wkb->length();
|
|
int closed;
|
|
|
|
if (wkb->reserve(4, 512))
|
|
return 1;
|
|
wkb->length(wkb->length()+4); // Reserve space for points
|
|
for (;;)
|
|
{
|
|
Gis_line_string ls;
|
|
uint32 ls_pos=wkb->length();
|
|
if (trs->check_next_symbol('(') ||
|
|
ls.init_from_wkt(trs, wkb) ||
|
|
trs->check_next_symbol(')'))
|
|
return 1;
|
|
|
|
ls.set_data_ptr(wkb->ptr() + ls_pos, wkb->length() - ls_pos);
|
|
if (ls.is_closed(&closed) || !closed)
|
|
{
|
|
trs->set_error_msg("POLYGON's linear ring isn't closed");
|
|
return 1;
|
|
}
|
|
n_linear_rings++;
|
|
if (trs->skip_char(',')) // Didn't find ','
|
|
break;
|
|
}
|
|
wkb->write_at_position(lr_pos, n_linear_rings);
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint Gis_polygon::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo,
|
|
String *res)
|
|
{
|
|
uint32 n_linear_rings;
|
|
const char *wkb_orig= wkb;
|
|
|
|
if (len < 4)
|
|
return 0;
|
|
|
|
n_linear_rings= wkb_get_uint(wkb, bo);
|
|
if (res->reserve(4, 512))
|
|
return 0;
|
|
wkb+= 4;
|
|
len-= 4;
|
|
res->q_append(n_linear_rings);
|
|
|
|
while (n_linear_rings--)
|
|
{
|
|
Gis_line_string ls;
|
|
uint32 ls_pos= res->length();
|
|
int ls_len;
|
|
int closed;
|
|
|
|
if (!(ls_len= ls.init_from_wkb(wkb, len, bo, res)))
|
|
return 0;
|
|
|
|
ls.set_data_ptr(res->ptr() + ls_pos, res->length() - ls_pos);
|
|
|
|
if (ls.is_closed(&closed) || !closed)
|
|
return 0;
|
|
wkb+= ls_len;
|
|
}
|
|
|
|
return (uint) (wkb - wkb_orig);
|
|
}
|
|
|
|
|
|
bool Gis_polygon::get_data_as_wkt(String *txt, const char **end) const
|
|
{
|
|
uint32 n_linear_rings;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
|
|
n_linear_rings= uint4korr(data);
|
|
data+= 4;
|
|
|
|
while (n_linear_rings--)
|
|
{
|
|
uint32 n_points;
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_points= uint4korr(data);
|
|
data+= 4;
|
|
if (no_data(data, (SIZEOF_STORED_DOUBLE*2) * n_points) ||
|
|
txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
|
|
return 1;
|
|
txt->qs_append('(');
|
|
data= append_points(txt, n_points, data, 0);
|
|
(*txt) [txt->length() - 1]= ')'; // Replace end ','
|
|
txt->qs_append(',');
|
|
}
|
|
txt->length(txt->length() - 1); // Remove end ','
|
|
*end= data;
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Gis_polygon::get_mbr(MBR *mbr, const char **end) const
|
|
{
|
|
uint32 n_linear_rings;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_linear_rings= uint4korr(data);
|
|
data+= 4;
|
|
|
|
while (n_linear_rings--)
|
|
{
|
|
if (!(data= get_mbr_for_points(mbr, data, 0)))
|
|
return 1;
|
|
}
|
|
*end= data;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_polygon::area(double *ar, const char **end_of_data) 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;
|
|
|
|
while (n_linear_rings--)
|
|
{
|
|
double prev_x, prev_y;
|
|
double lr_area= 0;
|
|
uint32 n_points;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_points= uint4korr(data);
|
|
if (no_data(data, (SIZEOF_STORED_DOUBLE*2) * n_points))
|
|
return 1;
|
|
get_point(&prev_x, &prev_y, data+4);
|
|
data+= (4+SIZEOF_STORED_DOUBLE*2);
|
|
|
|
while (--n_points) // One point is already read
|
|
{
|
|
double x, y;
|
|
get_point(&x, &y, data);
|
|
data+= (SIZEOF_STORED_DOUBLE*2);
|
|
lr_area+= (prev_x + x)* (prev_y - y);
|
|
prev_x= x;
|
|
prev_y= y;
|
|
}
|
|
lr_area= fabs(lr_area)/2;
|
|
if (result == -1.0)
|
|
result= lr_area;
|
|
else
|
|
result-= lr_area;
|
|
}
|
|
*ar= fabs(result);
|
|
*end_of_data= data;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_polygon::exterior_ring(String *result) const
|
|
{
|
|
uint32 n_points, length;
|
|
const char *data= m_data + 4; // skip n_linerings
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_points= uint4korr(data);
|
|
data+= 4;
|
|
length= n_points * POINT_DATA_SIZE;
|
|
if (no_data(data, length) || result->reserve(1+4+4+ length))
|
|
return 1;
|
|
|
|
result->q_append((char) wkb_ndr);
|
|
result->q_append((uint32) wkb_linestring);
|
|
result->q_append(n_points);
|
|
result->q_append(data, n_points * POINT_DATA_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_polygon::num_interior_ring(uint32 *n_int_rings) const
|
|
{
|
|
if (no_data(m_data, 4))
|
|
return 1;
|
|
*n_int_rings= uint4korr(m_data)-1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_polygon::interior_ring_n(uint32 num, String *result) const
|
|
{
|
|
const char *data= m_data;
|
|
uint32 n_linear_rings;
|
|
uint32 n_points;
|
|
uint32 points_size;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_linear_rings= uint4korr(data);
|
|
data+= 4;
|
|
|
|
if (num >= n_linear_rings || num < 1)
|
|
return 1;
|
|
|
|
while (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);
|
|
points_size= n_points * POINT_DATA_SIZE;
|
|
data+= 4;
|
|
if (no_data(data, points_size) || result->reserve(1+4+4+ points_size))
|
|
return 1;
|
|
|
|
result->q_append((char) wkb_ndr);
|
|
result->q_append((uint32) wkb_linestring);
|
|
result->q_append(n_points);
|
|
result->q_append(data, points_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_polygon::centroid_xy(double *x, double *y) const
|
|
{
|
|
uint32 n_linear_rings;
|
|
double UNINIT_VAR(res_area);
|
|
double UNINIT_VAR(res_cx), UNINIT_VAR(res_cy);
|
|
const char *data= m_data;
|
|
bool first_loop= 1;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_linear_rings= uint4korr(data);
|
|
data+= 4;
|
|
|
|
DBUG_ASSERT(n_linear_rings > 0);
|
|
|
|
while (n_linear_rings--)
|
|
{
|
|
uint32 n_points, org_n_points;
|
|
double prev_x, prev_y;
|
|
double cur_area= 0;
|
|
double cur_cx= 0;
|
|
double cur_cy= 0;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
org_n_points= n_points= uint4korr(data);
|
|
data+= 4;
|
|
if (no_data(data, (SIZEOF_STORED_DOUBLE*2) * n_points))
|
|
return 1;
|
|
get_point(&prev_x, &prev_y, data);
|
|
data+= (SIZEOF_STORED_DOUBLE*2);
|
|
|
|
while (--n_points) // One point is already read
|
|
{
|
|
double tmp_x, tmp_y;
|
|
get_point(&tmp_x, &tmp_y, data);
|
|
data+= (SIZEOF_STORED_DOUBLE*2);
|
|
cur_area+= (prev_x + tmp_x) * (prev_y - tmp_y);
|
|
cur_cx+= tmp_x;
|
|
cur_cy+= tmp_y;
|
|
prev_x= tmp_x;
|
|
prev_y= tmp_y;
|
|
}
|
|
cur_area= fabs(cur_area) / 2;
|
|
cur_cx= cur_cx / (org_n_points - 1);
|
|
cur_cy= cur_cy / (org_n_points - 1);
|
|
|
|
if (!first_loop)
|
|
{
|
|
double d_area= fabs(res_area - cur_area);
|
|
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
|
|
{
|
|
first_loop= 0;
|
|
res_area= cur_area;
|
|
res_cx= cur_cx;
|
|
res_cy= cur_cy;
|
|
}
|
|
}
|
|
|
|
*x= res_cx;
|
|
*y= res_cy;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_polygon::centroid(String *result) const
|
|
{
|
|
double x, y;
|
|
if (centroid_xy(&x, &y))
|
|
return 1;
|
|
return create_point(result, x, y);
|
|
}
|
|
|
|
const Geometry::Class_info *Gis_polygon::get_class_info() const
|
|
{
|
|
return &polygon_class;
|
|
}
|
|
|
|
|
|
/***************************** MultiPoint *******************************/
|
|
|
|
uint32 Gis_multi_point::get_data_size() const
|
|
{
|
|
if (no_data(m_data, 4))
|
|
return GET_SIZE_ERROR;
|
|
return 4 + uint4korr(m_data)*(POINT_DATA_SIZE + WKB_HEADER_SIZE);
|
|
}
|
|
|
|
|
|
bool Gis_multi_point::init_from_wkt(Gis_read_stream *trs, String *wkb)
|
|
{
|
|
uint32 n_points= 0;
|
|
uint32 np_pos= wkb->length();
|
|
Gis_point p;
|
|
|
|
if (wkb->reserve(4, 512))
|
|
return 1;
|
|
wkb->length(wkb->length()+4); // Reserve space for points
|
|
|
|
for (;;)
|
|
{
|
|
if (wkb->reserve(1+4, 512))
|
|
return 1;
|
|
wkb->q_append((char) wkb_ndr);
|
|
wkb->q_append((uint32) wkb_point);
|
|
if (p.init_from_wkt(trs, wkb))
|
|
return 1;
|
|
n_points++;
|
|
if (trs->skip_char(',')) // Didn't find ','
|
|
break;
|
|
}
|
|
wkb->write_at_position(np_pos, n_points); // Store number of found points
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint Gis_multi_point::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo,
|
|
String *res)
|
|
{
|
|
uint32 n_points;
|
|
uint proper_size;
|
|
Gis_point p;
|
|
const char *wkb_end;
|
|
|
|
if (len < 4)
|
|
return 0;
|
|
n_points= wkb_get_uint(wkb, bo);
|
|
proper_size= 4 + n_points * (WKB_HEADER_SIZE + POINT_DATA_SIZE);
|
|
|
|
if (len < proper_size || res->reserve(proper_size))
|
|
return 0;
|
|
|
|
res->q_append(n_points);
|
|
wkb_end= wkb + proper_size;
|
|
for (wkb+=4; wkb < wkb_end; wkb+= (WKB_HEADER_SIZE + POINT_DATA_SIZE))
|
|
{
|
|
res->q_append((char)wkb_ndr);
|
|
res->q_append((uint32)wkb_point);
|
|
if (!p.init_from_wkb(wkb + WKB_HEADER_SIZE,
|
|
POINT_DATA_SIZE, (wkbByteOrder) wkb[0], res))
|
|
return 0;
|
|
}
|
|
return proper_size;
|
|
}
|
|
|
|
|
|
bool Gis_multi_point::get_data_as_wkt(String *txt, const char **end) const
|
|
{
|
|
uint32 n_points;
|
|
if (no_data(m_data, 4))
|
|
return 1;
|
|
|
|
n_points= uint4korr(m_data);
|
|
if (no_data(m_data+4,
|
|
n_points * (SIZEOF_STORED_DOUBLE * 2 + WKB_HEADER_SIZE)) ||
|
|
txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
|
|
return 1;
|
|
*end= append_points(txt, n_points, m_data+4, WKB_HEADER_SIZE);
|
|
txt->length(txt->length()-1); // Remove end ','
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Gis_multi_point::get_mbr(MBR *mbr, const char **end) const
|
|
{
|
|
return (*end= get_mbr_for_points(mbr, m_data, WKB_HEADER_SIZE)) == 0;
|
|
}
|
|
|
|
|
|
int Gis_multi_point::num_geometries(uint32 *num) const
|
|
{
|
|
*num= uint4korr(m_data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_multi_point::geometry_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);
|
|
data+= 4+ (num - 1) * (WKB_HEADER_SIZE + POINT_DATA_SIZE);
|
|
|
|
if (num > n_points || num < 1 ||
|
|
no_data(data, WKB_HEADER_SIZE + POINT_DATA_SIZE) ||
|
|
result->reserve(WKB_HEADER_SIZE + POINT_DATA_SIZE))
|
|
return 1;
|
|
|
|
result->q_append(data, WKB_HEADER_SIZE + POINT_DATA_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
const Geometry::Class_info *Gis_multi_point::get_class_info() const
|
|
{
|
|
return &multipoint_class;
|
|
}
|
|
|
|
|
|
/***************************** MultiLineString *******************************/
|
|
|
|
uint32 Gis_multi_line_string::get_data_size() const
|
|
{
|
|
uint32 n_line_strings;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4))
|
|
return GET_SIZE_ERROR;
|
|
n_line_strings= uint4korr(data);
|
|
data+= 4;
|
|
|
|
while (n_line_strings--)
|
|
{
|
|
if (no_data(data, WKB_HEADER_SIZE + 4))
|
|
return GET_SIZE_ERROR;
|
|
data+= (WKB_HEADER_SIZE + 4 + uint4korr(data + WKB_HEADER_SIZE) *
|
|
POINT_DATA_SIZE);
|
|
}
|
|
return (uint32) (data - m_data);
|
|
}
|
|
|
|
|
|
bool Gis_multi_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb)
|
|
{
|
|
uint32 n_line_strings= 0;
|
|
uint32 ls_pos= wkb->length();
|
|
|
|
if (wkb->reserve(4, 512))
|
|
return 1;
|
|
wkb->length(wkb->length()+4); // Reserve space for points
|
|
|
|
for (;;)
|
|
{
|
|
Gis_line_string ls;
|
|
|
|
if (wkb->reserve(1+4, 512))
|
|
return 1;
|
|
wkb->q_append((char) wkb_ndr);
|
|
wkb->q_append((uint32) wkb_linestring);
|
|
|
|
if (trs->check_next_symbol('(') ||
|
|
ls.init_from_wkt(trs, wkb) ||
|
|
trs->check_next_symbol(')'))
|
|
return 1;
|
|
n_line_strings++;
|
|
if (trs->skip_char(',')) // Didn't find ','
|
|
break;
|
|
}
|
|
wkb->write_at_position(ls_pos, n_line_strings);
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint Gis_multi_line_string::init_from_wkb(const char *wkb, uint len,
|
|
wkbByteOrder bo, String *res)
|
|
{
|
|
uint32 n_line_strings;
|
|
const char *wkb_orig= wkb;
|
|
|
|
if (len < 4)
|
|
return 0;
|
|
n_line_strings= wkb_get_uint(wkb, bo);
|
|
|
|
if (res->reserve(4, 512))
|
|
return 0;
|
|
res->q_append(n_line_strings);
|
|
|
|
wkb+= 4;
|
|
while (n_line_strings--)
|
|
{
|
|
Gis_line_string ls;
|
|
int ls_len;
|
|
|
|
if ((len < WKB_HEADER_SIZE) ||
|
|
res->reserve(WKB_HEADER_SIZE, 512))
|
|
return 0;
|
|
|
|
res->q_append((char) wkb_ndr);
|
|
res->q_append((uint32) wkb_linestring);
|
|
|
|
if (!(ls_len= ls.init_from_wkb(wkb + WKB_HEADER_SIZE, len,
|
|
(wkbByteOrder) wkb[0], res)))
|
|
return 0;
|
|
ls_len+= WKB_HEADER_SIZE;;
|
|
wkb+= ls_len;
|
|
len-= ls_len;
|
|
}
|
|
return (uint) (wkb - wkb_orig);
|
|
}
|
|
|
|
|
|
bool Gis_multi_line_string::get_data_as_wkt(String *txt,
|
|
const char **end) const
|
|
{
|
|
uint32 n_line_strings;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_line_strings= uint4korr(data);
|
|
data+= 4;
|
|
|
|
while (n_line_strings--)
|
|
{
|
|
uint32 n_points;
|
|
if (no_data(data, (WKB_HEADER_SIZE + 4)))
|
|
return 1;
|
|
n_points= uint4korr(data + WKB_HEADER_SIZE);
|
|
data+= WKB_HEADER_SIZE + 4;
|
|
if (no_data(data, n_points * (SIZEOF_STORED_DOUBLE*2)) ||
|
|
txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
|
|
return 1;
|
|
txt->qs_append('(');
|
|
data= append_points(txt, n_points, data, 0);
|
|
(*txt) [txt->length() - 1]= ')';
|
|
txt->qs_append(',');
|
|
}
|
|
txt->length(txt->length() - 1);
|
|
*end= data;
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Gis_multi_line_string::get_mbr(MBR *mbr, const char **end) const
|
|
{
|
|
uint32 n_line_strings;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_line_strings= uint4korr(data);
|
|
data+= 4;
|
|
|
|
while (n_line_strings--)
|
|
{
|
|
data+= WKB_HEADER_SIZE;
|
|
if (!(data= get_mbr_for_points(mbr, data, 0)))
|
|
return 1;
|
|
}
|
|
*end= data;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_multi_line_string::num_geometries(uint32 *num) const
|
|
{
|
|
*num= uint4korr(m_data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_multi_line_string::geometry_n(uint32 num, String *result) const
|
|
{
|
|
uint32 n_line_strings, n_points, length;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_line_strings= uint4korr(data);
|
|
data+= 4;
|
|
|
|
if ((num > n_line_strings) || (num < 1))
|
|
return 1;
|
|
|
|
for (;;)
|
|
{
|
|
if (no_data(data, WKB_HEADER_SIZE + 4))
|
|
return 1;
|
|
n_points= uint4korr(data + WKB_HEADER_SIZE);
|
|
length= WKB_HEADER_SIZE + 4+ POINT_DATA_SIZE * n_points;
|
|
if (no_data(data, length))
|
|
return 1;
|
|
if (!--num)
|
|
break;
|
|
data+= length;
|
|
}
|
|
return result->append(data, length, (uint32) 0);
|
|
}
|
|
|
|
|
|
int Gis_multi_line_string::geom_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;
|
|
while (n_line_strings--)
|
|
{
|
|
double ls_len;
|
|
Gis_line_string ls;
|
|
data+= WKB_HEADER_SIZE;
|
|
ls.set_data_ptr(data, (uint32) (m_data_end - data));
|
|
if (ls.geom_length(&ls_len))
|
|
return 1;
|
|
*len+= ls_len;
|
|
/*
|
|
We know here that ls was ok, so we can call the trivial function
|
|
Gis_line_string::get_data_size without error checking
|
|
*/
|
|
data+= ls.get_data_size();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_multi_line_string::is_closed(int *closed) const
|
|
{
|
|
uint32 n_line_strings;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4 + WKB_HEADER_SIZE))
|
|
return 1;
|
|
n_line_strings= uint4korr(data);
|
|
data+= 4 + WKB_HEADER_SIZE;
|
|
|
|
while (n_line_strings--)
|
|
{
|
|
Gis_line_string ls;
|
|
if (no_data(data, 0))
|
|
return 1;
|
|
ls.set_data_ptr(data, (uint32) (m_data_end - data));
|
|
if (ls.is_closed(closed))
|
|
return 1;
|
|
if (!*closed)
|
|
return 0;
|
|
/*
|
|
We know here that ls was ok, so we can call the trivial function
|
|
Gis_line_string::get_data_size without error checking
|
|
*/
|
|
data+= ls.get_data_size() + WKB_HEADER_SIZE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const Geometry::Class_info *Gis_multi_line_string::get_class_info() const
|
|
{
|
|
return &multilinestring_class;
|
|
}
|
|
|
|
|
|
/***************************** MultiPolygon *******************************/
|
|
|
|
uint32 Gis_multi_polygon::get_data_size() const
|
|
{
|
|
uint32 n_polygons;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4))
|
|
return GET_SIZE_ERROR;
|
|
n_polygons= uint4korr(data);
|
|
data+= 4;
|
|
|
|
while (n_polygons--)
|
|
{
|
|
uint32 n_linear_rings;
|
|
if (no_data(data, 4 + WKB_HEADER_SIZE))
|
|
return GET_SIZE_ERROR;
|
|
|
|
n_linear_rings= uint4korr(data + WKB_HEADER_SIZE);
|
|
data+= 4 + WKB_HEADER_SIZE;
|
|
|
|
while (n_linear_rings--)
|
|
{
|
|
if (no_data(data, 4))
|
|
return GET_SIZE_ERROR;
|
|
data+= 4 + uint4korr(data) * POINT_DATA_SIZE;
|
|
}
|
|
}
|
|
return (uint32) (data - m_data);
|
|
}
|
|
|
|
|
|
bool Gis_multi_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb)
|
|
{
|
|
uint32 n_polygons= 0;
|
|
int np_pos= wkb->length();
|
|
Gis_polygon p;
|
|
|
|
if (wkb->reserve(4, 512))
|
|
return 1;
|
|
wkb->length(wkb->length()+4); // Reserve space for points
|
|
|
|
for (;;)
|
|
{
|
|
if (wkb->reserve(1+4, 512))
|
|
return 1;
|
|
wkb->q_append((char) wkb_ndr);
|
|
wkb->q_append((uint32) wkb_polygon);
|
|
|
|
if (trs->check_next_symbol('(') ||
|
|
p.init_from_wkt(trs, wkb) ||
|
|
trs->check_next_symbol(')'))
|
|
return 1;
|
|
n_polygons++;
|
|
if (trs->skip_char(',')) // Didn't find ','
|
|
break;
|
|
}
|
|
wkb->write_at_position(np_pos, n_polygons);
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint Gis_multi_polygon::init_from_wkb(const char *wkb, uint len,
|
|
wkbByteOrder bo, String *res)
|
|
{
|
|
uint32 n_poly;
|
|
const char *wkb_orig= wkb;
|
|
|
|
if (len < 4)
|
|
return 0;
|
|
n_poly= wkb_get_uint(wkb, bo);
|
|
|
|
if (res->reserve(4, 512))
|
|
return 0;
|
|
res->q_append(n_poly);
|
|
|
|
wkb+=4;
|
|
while (n_poly--)
|
|
{
|
|
Gis_polygon p;
|
|
int p_len;
|
|
|
|
if (len < WKB_HEADER_SIZE ||
|
|
res->reserve(WKB_HEADER_SIZE, 512))
|
|
return 0;
|
|
res->q_append((char) wkb_ndr);
|
|
res->q_append((uint32) wkb_polygon);
|
|
|
|
if (!(p_len= p.init_from_wkb(wkb + WKB_HEADER_SIZE, len,
|
|
(wkbByteOrder) wkb[0], res)))
|
|
return 0;
|
|
p_len+= WKB_HEADER_SIZE;
|
|
wkb+= p_len;
|
|
len-= p_len;
|
|
}
|
|
return (uint) (wkb - wkb_orig);
|
|
}
|
|
|
|
|
|
bool Gis_multi_polygon::get_data_as_wkt(String *txt, const char **end) const
|
|
{
|
|
uint32 n_polygons;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_polygons= uint4korr(data);
|
|
data+= 4;
|
|
|
|
while (n_polygons--)
|
|
{
|
|
uint32 n_linear_rings;
|
|
if (no_data(data, 4 + WKB_HEADER_SIZE) ||
|
|
txt->reserve(1, 512))
|
|
return 1;
|
|
n_linear_rings= uint4korr(data+WKB_HEADER_SIZE);
|
|
data+= 4 + WKB_HEADER_SIZE;
|
|
txt->q_append('(');
|
|
|
|
while (n_linear_rings--)
|
|
{
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
uint32 n_points= uint4korr(data);
|
|
data+= 4;
|
|
if (no_data(data, (SIZEOF_STORED_DOUBLE * 2) * n_points) ||
|
|
txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points,
|
|
512))
|
|
return 1;
|
|
txt->qs_append('(');
|
|
data= append_points(txt, n_points, data, 0);
|
|
(*txt) [txt->length() - 1]= ')';
|
|
txt->qs_append(',');
|
|
}
|
|
(*txt) [txt->length() - 1]= ')';
|
|
txt->qs_append(',');
|
|
}
|
|
txt->length(txt->length() - 1);
|
|
*end= data;
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Gis_multi_polygon::get_mbr(MBR *mbr, const char **end) const
|
|
{
|
|
uint32 n_polygons;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_polygons= uint4korr(data);
|
|
data+= 4;
|
|
|
|
while (n_polygons--)
|
|
{
|
|
uint32 n_linear_rings;
|
|
if (no_data(data, 4+WKB_HEADER_SIZE))
|
|
return 1;
|
|
n_linear_rings= uint4korr(data + WKB_HEADER_SIZE);
|
|
data+= WKB_HEADER_SIZE + 4;
|
|
|
|
while (n_linear_rings--)
|
|
{
|
|
if (!(data= get_mbr_for_points(mbr, data, 0)))
|
|
return 1;
|
|
}
|
|
}
|
|
*end= data;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_multi_polygon::num_geometries(uint32 *num) const
|
|
{
|
|
*num= uint4korr(m_data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_multi_polygon::geometry_n(uint32 num, String *result) const
|
|
{
|
|
uint32 n_polygons;
|
|
const char *data= m_data, *start_of_polygon;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_polygons= uint4korr(data);
|
|
data+= 4;
|
|
|
|
if (num > n_polygons || num < 1)
|
|
return -1;
|
|
|
|
do
|
|
{
|
|
uint32 n_linear_rings;
|
|
start_of_polygon= data;
|
|
|
|
if (no_data(data, WKB_HEADER_SIZE + 4))
|
|
return 1;
|
|
n_linear_rings= uint4korr(data + WKB_HEADER_SIZE);
|
|
data+= WKB_HEADER_SIZE + 4;
|
|
|
|
while (n_linear_rings--)
|
|
{
|
|
uint32 n_points;
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_points= uint4korr(data);
|
|
data+= 4 + POINT_DATA_SIZE * n_points;
|
|
}
|
|
} while (--num);
|
|
if (no_data(data, 0)) // We must check last segment
|
|
return 1;
|
|
return result->append(start_of_polygon, (uint32) (data - start_of_polygon),
|
|
(uint32) 0);
|
|
}
|
|
|
|
|
|
int Gis_multi_polygon::area(double *ar, const char **end_of_data) 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;
|
|
|
|
while (n_polygons--)
|
|
{
|
|
double p_area;
|
|
Gis_polygon p;
|
|
|
|
data+= WKB_HEADER_SIZE;
|
|
p.set_data_ptr(data, (uint32) (m_data_end - data));
|
|
if (p.area(&p_area, &data))
|
|
return 1;
|
|
result+= p_area;
|
|
}
|
|
*ar= result;
|
|
*end_of_data= data;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_multi_polygon::centroid(String *result) const
|
|
{
|
|
uint32 n_polygons;
|
|
bool first_loop= 1;
|
|
Gis_polygon p;
|
|
double UNINIT_VAR(res_area), UNINIT_VAR(res_cx), UNINIT_VAR(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;
|
|
|
|
while (n_polygons--)
|
|
{
|
|
data+= WKB_HEADER_SIZE;
|
|
p.set_data_ptr(data, (uint32) (m_data_end - data));
|
|
if (p.area(&cur_area, &data) ||
|
|
p.centroid_xy(&cur_cx, &cur_cy))
|
|
return 1;
|
|
|
|
if (!first_loop)
|
|
{
|
|
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
|
|
{
|
|
first_loop= 0;
|
|
res_area= cur_area;
|
|
res_cx= cur_cx;
|
|
res_cy= cur_cy;
|
|
}
|
|
}
|
|
|
|
return create_point(result, res_cx, res_cy);
|
|
}
|
|
|
|
const Geometry::Class_info *Gis_multi_polygon::get_class_info() const
|
|
{
|
|
return &multipolygon_class;
|
|
}
|
|
|
|
|
|
/************************* GeometryCollection ****************************/
|
|
|
|
uint32 Gis_geometry_collection::get_data_size() const
|
|
{
|
|
uint32 n_objects;
|
|
const char *data= m_data;
|
|
Geometry_buffer buffer;
|
|
Geometry *geom;
|
|
|
|
if (no_data(data, 4))
|
|
return GET_SIZE_ERROR;
|
|
n_objects= uint4korr(data);
|
|
data+= 4;
|
|
|
|
while (n_objects--)
|
|
{
|
|
uint32 wkb_type,object_size;
|
|
|
|
if (no_data(data, WKB_HEADER_SIZE))
|
|
return GET_SIZE_ERROR;
|
|
wkb_type= uint4korr(data + 1);
|
|
data+= WKB_HEADER_SIZE;
|
|
|
|
if (!(geom= create_by_typeid(&buffer, wkb_type)))
|
|
return GET_SIZE_ERROR;
|
|
geom->set_data_ptr(data, (uint) (m_data_end - data));
|
|
if ((object_size= geom->get_data_size()) == GET_SIZE_ERROR)
|
|
return GET_SIZE_ERROR;
|
|
data+= object_size;
|
|
}
|
|
return (uint32) (data - m_data);
|
|
}
|
|
|
|
|
|
bool Gis_geometry_collection::init_from_wkt(Gis_read_stream *trs, String *wkb)
|
|
{
|
|
uint32 n_objects= 0;
|
|
uint32 no_pos= wkb->length();
|
|
Geometry_buffer buffer;
|
|
Geometry *g;
|
|
|
|
if (wkb->reserve(4, 512))
|
|
return 1;
|
|
wkb->length(wkb->length()+4); // Reserve space for points
|
|
|
|
for (;;)
|
|
{
|
|
if (!(g= create_from_wkt(&buffer, trs, wkb)))
|
|
return 1;
|
|
|
|
if (g->get_class_info()->m_type_id == wkb_geometrycollection)
|
|
{
|
|
trs->set_error_msg("Unexpected GEOMETRYCOLLECTION");
|
|
return 1;
|
|
}
|
|
n_objects++;
|
|
if (trs->skip_char(',')) // Didn't find ','
|
|
break;
|
|
}
|
|
|
|
wkb->write_at_position(no_pos, n_objects);
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint Gis_geometry_collection::init_from_wkb(const char *wkb, uint len,
|
|
wkbByteOrder bo, String *res)
|
|
{
|
|
uint32 n_geom;
|
|
const char *wkb_orig= wkb;
|
|
|
|
if (len < 4)
|
|
return 0;
|
|
n_geom= wkb_get_uint(wkb, bo);
|
|
|
|
if (res->reserve(4, 512))
|
|
return 0;
|
|
res->q_append(n_geom);
|
|
|
|
wkb+= 4;
|
|
while (n_geom--)
|
|
{
|
|
Geometry_buffer buffer;
|
|
Geometry *geom;
|
|
int g_len;
|
|
uint32 wkb_type;
|
|
|
|
if (len < WKB_HEADER_SIZE ||
|
|
res->reserve(WKB_HEADER_SIZE, 512))
|
|
return 0;
|
|
|
|
res->q_append((char) wkb_ndr);
|
|
wkb_type= wkb_get_uint(wkb+1, (wkbByteOrder) wkb[0]);
|
|
res->q_append(wkb_type);
|
|
|
|
if (!(geom= create_by_typeid(&buffer, wkb_type)) ||
|
|
!(g_len= geom->init_from_wkb(wkb + WKB_HEADER_SIZE, len,
|
|
(wkbByteOrder) wkb[0], res)))
|
|
return 0;
|
|
g_len+= WKB_HEADER_SIZE;
|
|
wkb+= g_len;
|
|
len-= g_len;
|
|
}
|
|
return (uint) (wkb - wkb_orig);
|
|
}
|
|
|
|
|
|
bool Gis_geometry_collection::get_data_as_wkt(String *txt,
|
|
const char **end) const
|
|
{
|
|
uint32 n_objects;
|
|
Geometry_buffer buffer;
|
|
Geometry *geom;
|
|
const char *data= m_data;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_objects= uint4korr(data);
|
|
data+= 4;
|
|
|
|
while (n_objects--)
|
|
{
|
|
uint32 wkb_type;
|
|
|
|
if (no_data(data, WKB_HEADER_SIZE))
|
|
return 1;
|
|
wkb_type= uint4korr(data + 1);
|
|
data+= WKB_HEADER_SIZE;
|
|
|
|
if (!(geom= create_by_typeid(&buffer, wkb_type)))
|
|
return 1;
|
|
geom->set_data_ptr(data, (uint) (m_data_end - data));
|
|
if (geom->as_wkt(txt, &data))
|
|
return 1;
|
|
if (txt->append(STRING_WITH_LEN(","), 512))
|
|
return 1;
|
|
}
|
|
txt->length(txt->length() - 1);
|
|
*end= data;
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Gis_geometry_collection::get_mbr(MBR *mbr, const char **end) const
|
|
{
|
|
uint32 n_objects;
|
|
const char *data= m_data;
|
|
Geometry_buffer buffer;
|
|
Geometry *geom;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_objects= uint4korr(data);
|
|
data+= 4;
|
|
|
|
while (n_objects--)
|
|
{
|
|
uint32 wkb_type;
|
|
|
|
if (no_data(data, WKB_HEADER_SIZE))
|
|
return 1;
|
|
wkb_type= uint4korr(data + 1);
|
|
data+= WKB_HEADER_SIZE;
|
|
|
|
if (!(geom= create_by_typeid(&buffer, wkb_type)))
|
|
return 1;
|
|
geom->set_data_ptr(data, (uint32) (m_data_end - data));
|
|
if (geom->get_mbr(mbr, &data))
|
|
return 1;
|
|
}
|
|
*end= data;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_geometry_collection::num_geometries(uint32 *num) const
|
|
{
|
|
if (no_data(m_data, 4))
|
|
return 1;
|
|
*num= uint4korr(m_data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gis_geometry_collection::geometry_n(uint32 num, String *result) const
|
|
{
|
|
uint32 n_objects, wkb_type, length;
|
|
const char *data= m_data;
|
|
Geometry_buffer buffer;
|
|
Geometry *geom;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_objects= uint4korr(data);
|
|
data+= 4;
|
|
if (num > n_objects || num < 1)
|
|
return 1;
|
|
|
|
do
|
|
{
|
|
if (no_data(data, WKB_HEADER_SIZE))
|
|
return 1;
|
|
wkb_type= uint4korr(data + 1);
|
|
data+= WKB_HEADER_SIZE;
|
|
|
|
if (!(geom= create_by_typeid(&buffer, wkb_type)))
|
|
return 1;
|
|
geom->set_data_ptr(data, (uint) (m_data_end - data));
|
|
if ((length= geom->get_data_size()) == GET_SIZE_ERROR)
|
|
return 1;
|
|
data+= length;
|
|
} while (--num);
|
|
|
|
/* Copy found object to result */
|
|
if (result->reserve(1+4+length))
|
|
return 1;
|
|
result->q_append((char) wkb_ndr);
|
|
result->q_append((uint32) wkb_type);
|
|
result->q_append(data-length, length); // data-length = start_of_data
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Return dimension for object
|
|
|
|
SYNOPSIS
|
|
dimension()
|
|
res_dim Result dimension
|
|
end End of object will be stored here. May be 0 for
|
|
simple objects!
|
|
RETURN
|
|
0 ok
|
|
1 error
|
|
*/
|
|
|
|
bool Gis_geometry_collection::dimension(uint32 *res_dim, const char **end) const
|
|
{
|
|
uint32 n_objects;
|
|
const char *data= m_data;
|
|
Geometry_buffer buffer;
|
|
Geometry *geom;
|
|
|
|
if (no_data(data, 4))
|
|
return 1;
|
|
n_objects= uint4korr(data);
|
|
data+= 4;
|
|
|
|
*res_dim= 0;
|
|
while (n_objects--)
|
|
{
|
|
uint32 wkb_type, length, dim;
|
|
const char *end_data;
|
|
|
|
if (no_data(data, WKB_HEADER_SIZE))
|
|
return 1;
|
|
wkb_type= uint4korr(data + 1);
|
|
data+= WKB_HEADER_SIZE;
|
|
if (!(geom= create_by_typeid(&buffer, wkb_type)))
|
|
return 1;
|
|
geom->set_data_ptr(data, (uint32) (m_data_end - data));
|
|
if (geom->dimension(&dim, &end_data))
|
|
return 1;
|
|
set_if_bigger(*res_dim, dim);
|
|
if (end_data) // Complex object
|
|
data= end_data;
|
|
else if ((length= geom->get_data_size()) == GET_SIZE_ERROR)
|
|
return 1;
|
|
else
|
|
data+= length;
|
|
}
|
|
*end= data;
|
|
return 0;
|
|
}
|
|
|
|
const Geometry::Class_info *Gis_geometry_collection::get_class_info() const
|
|
{
|
|
return &geometrycollection_class;
|
|
}
|
|
|
|
#endif /*HAVE_SPATIAL*/
|